PostgreSQL Source Code  git master
pg_stat_statements.c File Reference
#include "postgres.h"
#include <math.h>
#include <sys/stat.h>
#include <unistd.h>
#include "catalog/pg_authid.h"
#include "common/hashfn.h"
#include "executor/instrument.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "optimizer/planner.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
#include "parser/scanner.h"
#include "parser/scansup.h"
#include "pgstat.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/spin.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
Include dependency graph for pg_stat_statements.c:

Go to the source code of this file.

Data Structures

struct  pgssHashKey
 
struct  Counters
 
struct  pgssGlobalStats
 
struct  pgssEntry
 
struct  pgssSharedState
 
struct  pgssLocationLen
 
struct  pgssJumbleState
 

Macros

#define PGSS_DUMP_FILE   PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"
 
#define PGSS_TEXT_FILE   PG_STAT_TMP_DIR "/pgss_query_texts.stat"
 
#define USAGE_EXEC(duration)   (1.0)
 
#define USAGE_INIT   (1.0) /* including initial planning */
 
#define ASSUMED_MEDIAN_INIT   (10.0) /* initial assumed median usage */
 
#define ASSUMED_LENGTH_INIT   1024 /* initial assumed mean query length */
 
#define USAGE_DECREASE_FACTOR   (0.99) /* decreased every entry_dealloc */
 
#define STICKY_DECREASE_FACTOR   (0.50) /* factor for sticky entries */
 
#define USAGE_DEALLOC_PERCENT   5 /* free this % of entries at once */
 
#define IS_STICKY(c)   ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)
 
#define JUMBLE_SIZE   1024 /* query serialization buffer size */
 
#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   32 /* maximum of above */
 
#define PG_STAT_STATEMENTS_INFO_COLS   2
 
#define APP_JUMB(item)   AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))
 
#define APP_JUMB_STRING(str)   AppendJumble(jstate, (const unsigned char *) (str), strlen(str) + 1)
 

Typedefs

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

Enumerations

enum  pgssVersion {
  PGSS_V1_0 = 0, PGSS_V1_1, PGSS_V1_2, PGSS_V1_3,
  PGSS_V1_8
}
 
enum  pgssStoreKind { PGSS_INVALID = -1, PGSS_PLAN = 0, PGSS_EXEC, PGSS_NUMKIND }
 
enum  PGSSTrackLevel { PGSS_TRACK_NONE, PGSS_TRACK_TOP, PGSS_TRACK_ALL }
 

Functions

void _PG_init (void)
 
void _PG_fini (void)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_reset)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_reset_1_7)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_1_2)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_1_3)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_1_8)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_info)
 
static void pgss_shmem_startup (void)
 
static void pgss_shmem_shutdown (int code, Datum arg)
 
static void pgss_post_parse_analyze (ParseState *pstate, Query *query)
 
static PlannedStmtpgss_planner (Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
 
static void pgss_ExecutorStart (QueryDesc *queryDesc, int eflags)
 
static void pgss_ExecutorRun (QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
 
static void pgss_ExecutorFinish (QueryDesc *queryDesc)
 
static void pgss_ExecutorEnd (QueryDesc *queryDesc)
 
static void pgss_ProcessUtility (PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
 
static uint64 pgss_hash_string (const char *str, int len)
 
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, pgssJumbleState *jstate)
 
static void pg_stat_statements_internal (FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
 
static Size pgss_memsize (void)
 
static pgssEntryentry_alloc (pgssHashKey *key, Size query_offset, int query_len, int encoding, bool sticky)
 
static void entry_dealloc (void)
 
static bool qtext_store (const char *query, int query_len, Size *query_offset, int *gc_count)
 
static char * qtext_load_file (Size *buffer_size)
 
static char * qtext_fetch (Size query_offset, int query_len, char *buffer, Size buffer_size)
 
static bool need_gc_qtexts (void)
 
static void gc_qtexts (void)
 
static void entry_reset (Oid userid, Oid dbid, uint64 queryid)
 
static void AppendJumble (pgssJumbleState *jstate, const unsigned char *item, Size size)
 
static void JumbleQuery (pgssJumbleState *jstate, Query *query)
 
static void JumbleRangeTable (pgssJumbleState *jstate, List *rtable)
 
static void JumbleRowMarks (pgssJumbleState *jstate, List *rowMarks)
 
static void JumbleExpr (pgssJumbleState *jstate, Node *node)
 
static void RecordConstLocation (pgssJumbleState *jstate, int location)
 
static char * generate_normalized_query (pgssJumbleState *jstate, const char *query, int query_loc, int *query_len_p)
 
static void fill_in_constant_lengths (pgssJumbleState *jstate, const char *query, int query_loc)
 
static int comp_location (const void *a, const void *b)
 
Datum pg_stat_statements_reset_1_7 (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements_reset (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements_1_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 = 0x20201218
 
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100
 
static int exec_nested_level = 0
 
static int plan_nested_level = 0
 
static shmem_startup_hook_type prev_shmem_startup_hook = NULL
 
static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL
 
static planner_hook_type prev_planner_hook = NULL
 
static ExecutorStart_hook_type prev_ExecutorStart = NULL
 
static ExecutorRun_hook_type prev_ExecutorRun = NULL
 
static ExecutorFinish_hook_type prev_ExecutorFinish = NULL
 
static ExecutorEnd_hook_type prev_ExecutorEnd = NULL
 
static ProcessUtility_hook_type prev_ProcessUtility = NULL
 
static pgssSharedStatepgss = NULL
 
static HTABpgss_hash = NULL
 
static const struct config_enum_entry track_options []
 
static int pgss_max
 
static int pgss_track
 
static bool pgss_track_utility
 
static bool pgss_track_planning
 
static bool pgss_save
 

Macro Definition Documentation

◆ APP_JUMB

#define APP_JUMB (   item)    AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))

Definition at line 2671 of file pg_stat_statements.c.

Referenced by JumbleExpr(), JumbleQuery(), JumbleRangeTable(), and JumbleRowMarks().

◆ APP_JUMB_STRING

#define APP_JUMB_STRING (   str)    AppendJumble(jstate, (const unsigned char *) (str), strlen(str) + 1)

Definition at line 2673 of file pg_stat_statements.c.

Referenced by JumbleExpr(), and JumbleRangeTable().

◆ ASSUMED_LENGTH_INIT

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

Definition at line 111 of file pg_stat_statements.c.

Referenced by entry_dealloc(), gc_qtexts(), and pgss_shmem_startup().

◆ ASSUMED_MEDIAN_INIT

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

Definition at line 110 of file pg_stat_statements.c.

Referenced by pgss_shmem_startup().

◆ IS_STICKY

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

◆ JUMBLE_SIZE

#define JUMBLE_SIZE   1024 /* query serialization buffer size */

Definition at line 117 of file pg_stat_statements.c.

Referenced by AppendJumble(), and pgss_post_parse_analyze().

◆ PG_STAT_STATEMENTS_COLS

#define PG_STAT_STATEMENTS_COLS   32 /* maximum of above */

Definition at line 1514 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_0

#define PG_STAT_STATEMENTS_COLS_V1_0   14

Definition at line 1509 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_1

#define PG_STAT_STATEMENTS_COLS_V1_1   18

Definition at line 1510 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_2

#define PG_STAT_STATEMENTS_COLS_V1_2   19

Definition at line 1511 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_3

#define PG_STAT_STATEMENTS_COLS_V1_3   23

Definition at line 1512 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_8

#define PG_STAT_STATEMENTS_COLS_V1_8   32

Definition at line 1513 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_INFO_COLS

#define PG_STAT_STATEMENTS_INFO_COLS   2

Definition at line 1888 of file pg_stat_statements.c.

Referenced by pg_stat_statements_info().

◆ PGSS_DUMP_FILE

#define PGSS_DUMP_FILE   PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"

Definition at line 89 of file pg_stat_statements.c.

Referenced by pgss_shmem_shutdown(), and pgss_shmem_startup().

◆ pgss_enabled

#define pgss_enabled (   level)

◆ PGSS_TEXT_FILE

#define PGSS_TEXT_FILE   PG_STAT_TMP_DIR "/pgss_query_texts.stat"

◆ record_gc_qtexts

#define record_gc_qtexts ( )
Value:
do { \
volatile pgssSharedState *s = (volatile pgssSharedState *) pgss; \
SpinLockAcquire(&s->mutex); \
s->gc_count++; \
SpinLockRelease(&s->mutex); \
} while(0)
static pgssSharedState * pgss

Definition at line 322 of file pg_stat_statements.c.

Referenced by entry_reset(), and gc_qtexts().

◆ STICKY_DECREASE_FACTOR

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

Definition at line 113 of file pg_stat_statements.c.

Referenced by entry_dealloc().

◆ USAGE_DEALLOC_PERCENT

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

Definition at line 114 of file pg_stat_statements.c.

Referenced by entry_dealloc().

◆ USAGE_DECREASE_FACTOR

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

Definition at line 112 of file pg_stat_statements.c.

Referenced by entry_dealloc().

◆ USAGE_EXEC

#define USAGE_EXEC (   duration)    (1.0)

Definition at line 108 of file pg_stat_statements.c.

Referenced by pgss_store().

◆ USAGE_INIT

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

Definition at line 109 of file pg_stat_statements.c.

Referenced by entry_alloc(), and pgss_store().

Typedef Documentation

◆ Counters

typedef struct Counters Counters

◆ pgssEntry

typedef struct pgssEntry pgssEntry

◆ pgssGlobalStats

◆ pgssHashKey

typedef struct pgssHashKey pgssHashKey

◆ pgssJumbleState

◆ pgssLocationLen

◆ pgssSharedState

◆ pgssStoreKind

◆ pgssVersion

typedef enum pgssVersion pgssVersion

Enumeration Type Documentation

◆ pgssStoreKind

Enumerator
PGSS_INVALID 
PGSS_PLAN 
PGSS_EXEC 
PGSS_NUMKIND 

Definition at line 131 of file pg_stat_statements.c.

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

◆ PGSSTrackLevel

Enumerator
PGSS_TRACK_NONE 
PGSS_TRACK_TOP 
PGSS_TRACK_ALL 

Definition at line 296 of file pg_stat_statements.c.

297 {
298  PGSS_TRACK_NONE, /* track no statements */
299  PGSS_TRACK_TOP, /* only top level statements */
300  PGSS_TRACK_ALL /* all statements, including nested ones */
PGSSTrackLevel

◆ pgssVersion

Enumerator
PGSS_V1_0 
PGSS_V1_1 
PGSS_V1_2 
PGSS_V1_3 
PGSS_V1_8 

Definition at line 122 of file pg_stat_statements.c.

Function Documentation

◆ _PG_fini()

void _PG_fini ( void  )

Definition at line 510 of file pg_stat_statements.c.

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

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

◆ _PG_init()

void _PG_init ( void  )

Definition at line 401 of file pg_stat_statements.c.

References DefineCustomBoolVariable(), DefineCustomEnumVariable(), DefineCustomIntVariable(), EmitWarningsOnPlaceholders(), ExecutorEnd_hook, ExecutorFinish_hook, ExecutorRun_hook, ExecutorStart_hook, PGC_POSTMASTER, PGC_SIGHUP, PGC_SUSET, pgss_ExecutorEnd(), pgss_ExecutorFinish(), pgss_ExecutorRun(), pgss_ExecutorStart(), pgss_max, pgss_memsize(), pgss_planner(), pgss_post_parse_analyze(), pgss_ProcessUtility(), pgss_save, pgss_shmem_startup(), pgss_track, pgss_track_planning, PGSS_TRACK_TOP, pgss_track_utility, planner_hook, post_parse_analyze_hook, prev_ExecutorEnd, prev_ExecutorFinish, prev_ExecutorRun, prev_ExecutorStart, prev_planner_hook, prev_post_parse_analyze_hook, prev_ProcessUtility, prev_shmem_startup_hook, process_shared_preload_libraries_in_progress, ProcessUtility_hook, RequestAddinShmemSpace(), RequestNamedLWLockTranche(), and shmem_startup_hook.

402 {
403  /*
404  * In order to create our shared memory area, we have to be loaded via
405  * shared_preload_libraries. If not, fall out without hooking into any of
406  * the main system. (We don't throw error here because it seems useful to
407  * allow the pg_stat_statements functions to be created even when the
408  * module isn't active. The functions must protect themselves against
409  * being called then, however.)
410  */
412  return;
413 
414  /*
415  * Define (or redefine) custom GUC variables.
416  */
417  DefineCustomIntVariable("pg_stat_statements.max",
418  "Sets the maximum number of statements tracked by pg_stat_statements.",
419  NULL,
420  &pgss_max,
421  5000,
422  100,
423  INT_MAX,
425  0,
426  NULL,
427  NULL,
428  NULL);
429 
430  DefineCustomEnumVariable("pg_stat_statements.track",
431  "Selects which statements are tracked by pg_stat_statements.",
432  NULL,
433  &pgss_track,
436  PGC_SUSET,
437  0,
438  NULL,
439  NULL,
440  NULL);
441 
442  DefineCustomBoolVariable("pg_stat_statements.track_utility",
443  "Selects whether utility commands are tracked by pg_stat_statements.",
444  NULL,
446  true,
447  PGC_SUSET,
448  0,
449  NULL,
450  NULL,
451  NULL);
452 
453  DefineCustomBoolVariable("pg_stat_statements.track_planning",
454  "Selects whether planning duration is tracked by pg_stat_statements.",
455  NULL,
457  false,
458  PGC_SUSET,
459  0,
460  NULL,
461  NULL,
462  NULL);
463 
464  DefineCustomBoolVariable("pg_stat_statements.save",
465  "Save pg_stat_statements statistics across server shutdowns.",
466  NULL,
467  &pgss_save,
468  true,
469  PGC_SIGHUP,
470  0,
471  NULL,
472  NULL,
473  NULL);
474 
475  EmitWarningsOnPlaceholders("pg_stat_statements");
476 
477  /*
478  * Request additional shared resources. (These are no-ops if we're not in
479  * the postmaster process.) We'll allocate or attach to the shared
480  * resources in pgss_shmem_startup().
481  */
483  RequestNamedLWLockTranche("pg_stat_statements", 1);
484 
485  /*
486  * Install hooks.
487  */
504 }
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:9000
void RequestAddinShmemSpace(Size size)
Definition: ipci.c:71
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1598
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
Definition: lwlock.c:703
static ProcessUtility_hook_type prev_ProcessUtility
static void pgss_shmem_startup(void)
static ExecutorRun_hook_type prev_ExecutorRun
static ExecutorEnd_hook_type prev_ExecutorEnd
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c:76
static int pgss_track
static ExecutorStart_hook_type prev_ExecutorStart
static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
void DefineCustomEnumVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, int bootValue, const struct config_enum_entry *options, GucContext context, int flags, GucEnumCheckHook check_hook, GucEnumAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:9085
ExecutorStart_hook_type ExecutorStart_hook
Definition: execMain.c:70
static planner_hook_type prev_planner_hook
static bool pgss_track_planning
static void pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
static int pgss_max
ExecutorRun_hook_type ExecutorRun_hook
Definition: execMain.c:71
ExecutorEnd_hook_type ExecutorEnd_hook
Definition: execMain.c:73
static void pgss_post_parse_analyze(ParseState *pstate, Query *query)
Definition: guc.h:75
void EmitWarningsOnPlaceholders(const char *className)
Definition: guc.c:9113
static ExecutorFinish_hook_type prev_ExecutorFinish
planner_hook_type planner_hook
Definition: planner.c:74
Definition: guc.h:72
static PlannedStmt * pgss_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
static Size pgss_memsize(void)
shmem_startup_hook_type shmem_startup_hook
Definition: ipci.c:53
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
static bool pgss_track_utility
static shmem_startup_hook_type prev_shmem_startup_hook
static void pgss_ExecutorFinish(QueryDesc *queryDesc)
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:72
static void pgss_ExecutorEnd(QueryDesc *queryDesc)
void DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, bool bootValue, GucContext context, int flags, GucBoolCheckHook check_hook, GucBoolAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:8974
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:53
static const struct config_enum_entry track_options[]
static bool pgss_save

◆ AppendJumble()

static void AppendJumble ( pgssJumbleState jstate,
const unsigned char *  item,
Size  size 
)
static

Definition at line 2635 of file pg_stat_statements.c.

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

2636 {
2637  unsigned char *jumble = jstate->jumble;
2638  Size jumble_len = jstate->jumble_len;
2639 
2640  /*
2641  * Whenever the jumble buffer is full, we hash the current contents and
2642  * reset the buffer to contain just that hash value, thus relying on the
2643  * hash to summarize everything so far.
2644  */
2645  while (size > 0)
2646  {
2647  Size part_size;
2648 
2649  if (jumble_len >= JUMBLE_SIZE)
2650  {
2651  uint64 start_hash;
2652 
2653  start_hash = DatumGetUInt64(hash_any_extended(jumble,
2654  JUMBLE_SIZE, 0));
2655  memcpy(jumble, &start_hash, sizeof(start_hash));
2656  jumble_len = sizeof(start_hash);
2657  }
2658  part_size = Min(size, JUMBLE_SIZE - jumble_len);
2659  memcpy(jumble + jumble_len, item, part_size);
2660  jumble_len += part_size;
2661  item += part_size;
2662  size -= part_size;
2663  }
2664  jstate->jumble_len = jumble_len;
2665 }
#define Min(x, y)
Definition: c.h:986
unsigned char * jumble
#define JUMBLE_SIZE
static Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition: hashfn.h:37
#define DatumGetUInt64(X)
Definition: postgres.h:634
size_t Size
Definition: c.h:540

◆ comp_location()

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

Definition at line 3529 of file pg_stat_statements.c.

Referenced by fill_in_constant_lengths().

3530 {
3531  int l = ((const pgssLocationLen *) a)->location;
3532  int r = ((const pgssLocationLen *) b)->location;
3533 
3534  if (l < r)
3535  return -1;
3536  else if (l > r)
3537  return +1;
3538  else
3539  return 0;
3540 }

◆ entry_alloc()

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

Definition at line 1960 of file pg_stat_statements.c.

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

Referenced by pgss_shmem_startup(), and pgss_store().

1962 {
1963  pgssEntry *entry;
1964  bool found;
1965 
1966  /* Make space if needed */
1968  entry_dealloc();
1969 
1970  /* Find or create an entry with desired hash code */
1971  entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
1972 
1973  if (!found)
1974  {
1975  /* New entry, initialize it */
1976 
1977  /* reset the statistics */
1978  memset(&entry->counters, 0, sizeof(Counters));
1979  /* set the appropriate initial usage count */
1980  entry->counters.usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
1981  /* re-initialize the mutex each time ... we assume no one using it */
1982  SpinLockInit(&entry->mutex);
1983  /* ... and don't forget the query text metadata */
1984  Assert(query_len >= 0);
1985  entry->query_offset = query_offset;
1986  entry->query_len = query_len;
1987  entry->encoding = encoding;
1988  }
1989 
1990  return entry;
1991 }
#define SpinLockInit(lock)
Definition: spin.h:60
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1382
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
Counters counters
static pgssSharedState * pgss
static int pgss_max
static void entry_dealloc(void)
static HTAB * pgss_hash
#define USAGE_INIT
#define Assert(condition)
Definition: c.h:804
int32 encoding
Definition: pg_database.h:41

◆ entry_cmp()

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

Definition at line 1997 of file pg_stat_statements.c.

Referenced by entry_dealloc().

1998 {
1999  double l_usage = (*(pgssEntry *const *) lhs)->counters.usage;
2000  double r_usage = (*(pgssEntry *const *) rhs)->counters.usage;
2001 
2002  if (l_usage < r_usage)
2003  return -1;
2004  else if (l_usage > r_usage)
2005  return +1;
2006  else
2007  return 0;
2008 }

◆ entry_dealloc()

static void entry_dealloc ( void  )
static

Definition at line 2016 of file pg_stat_statements.c.

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

Referenced by entry_alloc().

2017 {
2018  HASH_SEQ_STATUS hash_seq;
2019  pgssEntry **entries;
2020  pgssEntry *entry;
2021  int nvictims;
2022  int i;
2023  Size tottextlen;
2024  int nvalidtexts;
2025 
2026  /*
2027  * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
2028  * While we're scanning the table, apply the decay factor to the usage
2029  * values, and update the mean query length.
2030  *
2031  * Note that the mean query length is almost immediately obsolete, since
2032  * we compute it before not after discarding the least-used entries.
2033  * Hopefully, that doesn't affect the mean too much; it doesn't seem worth
2034  * making two passes to get a more current result. Likewise, the new
2035  * cur_median_usage includes the entries we're about to zap.
2036  */
2037 
2038  entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
2039 
2040  i = 0;
2041  tottextlen = 0;
2042  nvalidtexts = 0;
2043 
2044  hash_seq_init(&hash_seq, pgss_hash);
2045  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2046  {
2047  entries[i++] = entry;
2048  /* "Sticky" entries get a different usage decay rate. */
2049  if (IS_STICKY(entry->counters))
2051  else
2053  /* In the mean length computation, ignore dropped texts. */
2054  if (entry->query_len >= 0)
2055  {
2056  tottextlen += entry->query_len + 1;
2057  nvalidtexts++;
2058  }
2059  }
2060 
2061  /* Sort into increasing order by usage */
2062  qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
2063 
2064  /* Record the (approximate) median usage */
2065  if (i > 0)
2066  pgss->cur_median_usage = entries[i / 2]->counters.usage;
2067  /* Record the mean query length */
2068  if (nvalidtexts > 0)
2069  pgss->mean_query_len = tottextlen / nvalidtexts;
2070  else
2072 
2073  /* Now zap an appropriate fraction of lowest-usage entries */
2074  nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
2075  nvictims = Min(nvictims, i);
2076 
2077  for (i = 0; i < nvictims; i++)
2078  {
2079  hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
2080  }
2081 
2082  pfree(entries);
2083 
2084  /* Increment the number of times entries are deallocated */
2085  {
2086  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2087 
2088  SpinLockAcquire(&s->mutex);
2089  s->stats.dealloc += 1;
2090  SpinLockRelease(&s->mutex);
2091  }
2092 }
#define USAGE_DEALLOC_PERCENT
#define Min(x, y)
Definition: c.h:986
static int entry_cmp(const void *lhs, const void *rhs)
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1382
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
Counters counters
#define ASSUMED_LENGTH_INIT
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
void pfree(void *pointer)
Definition: mcxt.c:1057
static HTAB * pgss_hash
#define IS_STICKY(c)
#define STICKY_DECREASE_FACTOR
#define USAGE_DECREASE_FACTOR
pgssGlobalStats stats
#define SpinLockRelease(lock)
Definition: spin.h:64
#define Max(x, y)
Definition: c.h:980
size_t Size
Definition: c.h:540
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
void * palloc(Size size)
Definition: mcxt.c:950
int i
#define qsort(a, b, c, d)
Definition: port.h:504

◆ entry_reset()

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

Definition at line 2525 of file pg_stat_statements.c.

References AllocateFile(), pgssHashKey::dbid, pgssGlobalStats::dealloc, ereport, errcode(), errcode_for_file_access(), errmsg(), ERROR, pgssSharedState::extent, FreeFile(), ftruncate, GetCurrentTimestamp(), hash_get_num_entries(), HASH_REMOVE, hash_search(), hash_seq_init(), hash_seq_search(), sort-test::key, pgssEntry::key, pgssSharedState::lock, LOG, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), pgssSharedState::mutex, PG_BINARY_W, PGSS_TEXT_FILE, pgssHashKey::queryid, record_gc_qtexts, SpinLockAcquire, SpinLockRelease, pgssSharedState::stats, pgssGlobalStats::stats_reset, and pgssHashKey::userid.

Referenced by pg_stat_statements_reset(), and pg_stat_statements_reset_1_7().

2526 {
2527  HASH_SEQ_STATUS hash_seq;
2528  pgssEntry *entry;
2529  FILE *qfile;
2530  long num_entries;
2531  long num_remove = 0;
2532  pgssHashKey key;
2533 
2534  if (!pgss || !pgss_hash)
2535  ereport(ERROR,
2536  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2537  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
2538 
2540  num_entries = hash_get_num_entries(pgss_hash);
2541 
2542  if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
2543  {
2544  /* If all the parameters are available, use the fast path. */
2545  key.userid = userid;
2546  key.dbid = dbid;
2547  key.queryid = queryid;
2548 
2549  /* Remove the key if exists */
2550  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
2551  if (entry) /* found */
2552  num_remove++;
2553  }
2554  else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
2555  {
2556  /* Remove entries corresponding to valid parameters. */
2557  hash_seq_init(&hash_seq, pgss_hash);
2558  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2559  {
2560  if ((!userid || entry->key.userid == userid) &&
2561  (!dbid || entry->key.dbid == dbid) &&
2562  (!queryid || entry->key.queryid == queryid))
2563  {
2564  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2565  num_remove++;
2566  }
2567  }
2568  }
2569  else
2570  {
2571  /* Remove all entries. */
2572  hash_seq_init(&hash_seq, pgss_hash);
2573  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2574  {
2575  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2576  num_remove++;
2577  }
2578  }
2579 
2580  /* All entries are removed? */
2581  if (num_entries != num_remove)
2582  goto release_lock;
2583 
2584  /*
2585  * Reset global statistics for pg_stat_statements since all entries are
2586  * removed.
2587  */
2588  {
2589  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2590  TimestampTz stats_reset = GetCurrentTimestamp();
2591 
2592  SpinLockAcquire(&s->mutex);
2593  s->stats.dealloc = 0;
2594  s->stats.stats_reset = stats_reset;
2595  SpinLockRelease(&s->mutex);
2596  }
2597 
2598  /*
2599  * Write new empty query file, perhaps even creating a new one to recover
2600  * if the file was missing.
2601  */
2603  if (qfile == NULL)
2604  {
2605  ereport(LOG,
2607  errmsg("could not create file \"%s\": %m",
2608  PGSS_TEXT_FILE)));
2609  goto done;
2610  }
2611 
2612  /* If ftruncate fails, log it, but it's not a fatal problem */
2613  if (ftruncate(fileno(qfile), 0) != 0)
2614  ereport(LOG,
2616  errmsg("could not truncate file \"%s\": %m",
2617  PGSS_TEXT_FILE)));
2618 
2619  FreeFile(qfile);
2620 
2621 done:
2622  pgss->extent = 0;
2623  /* This counts as a query text garbage collection for our purposes */
2624  record_gc_qtexts();
2625 
2626 release_lock:
2628 }
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1578
int64 TimestampTz
Definition: timestamp.h:39
int errcode(int sqlerrcode)
Definition: elog.c:694
#define PG_BINARY_W
Definition: c.h:1274
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1382
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
#define LOG
Definition: elog.h:26
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1808
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
static HTAB * pgss_hash
#define ERROR
Definition: elog.h:45
int errcode_for_file_access(void)
Definition: elog.c:717
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2354
pgssGlobalStats stats
#define SpinLockRelease(lock)
Definition: spin.h:64
pgssHashKey key
#define ereport(elevel,...)
Definition: elog.h:155
TimestampTz stats_reset
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1206
#define record_gc_qtexts()
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
int FreeFile(FILE *file)
Definition: fd.c:2553
int errmsg(const char *fmt,...)
Definition: elog.c:905
#define PGSS_TEXT_FILE
#define ftruncate(a, b)
Definition: win32_port.h:65

◆ fill_in_constant_lengths()

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

Definition at line 3429 of file pg_stat_statements.c.

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

Referenced by generate_normalized_query().

3431 {
3432  pgssLocationLen *locs;
3434  core_yy_extra_type yyextra;
3435  core_YYSTYPE yylval;
3436  YYLTYPE yylloc;
3437  int last_loc = -1;
3438  int i;
3439 
3440  /*
3441  * Sort the records by location so that we can process them in order while
3442  * scanning the query text.
3443  */
3444  if (jstate->clocations_count > 1)
3445  qsort(jstate->clocations, jstate->clocations_count,
3446  sizeof(pgssLocationLen), comp_location);
3447  locs = jstate->clocations;
3448 
3449  /* initialize the flex scanner --- should match raw_parser() */
3450  yyscanner = scanner_init(query,
3451  &yyextra,
3452  &ScanKeywords,
3454 
3455  /* we don't want to re-emit any escape string warnings */
3456  yyextra.escape_string_warning = false;
3457 
3458  /* Search for each constant, in sequence */
3459  for (i = 0; i < jstate->clocations_count; i++)
3460  {
3461  int loc = locs[i].location;
3462  int tok;
3463 
3464  /* Adjust recorded location if we're dealing with partial string */
3465  loc -= query_loc;
3466 
3467  Assert(loc >= 0);
3468 
3469  if (loc <= last_loc)
3470  continue; /* Duplicate constant, ignore */
3471 
3472  /* Lex tokens until we find the desired constant */
3473  for (;;)
3474  {
3475  tok = core_yylex(&yylval, &yylloc, yyscanner);
3476 
3477  /* We should not hit end-of-string, but if we do, behave sanely */
3478  if (tok == 0)
3479  break; /* out of inner for-loop */
3480 
3481  /*
3482  * We should find the token position exactly, but if we somehow
3483  * run past it, work with that.
3484  */
3485  if (yylloc >= loc)
3486  {
3487  if (query[loc] == '-')
3488  {
3489  /*
3490  * It's a negative value - this is the one and only case
3491  * where we replace more than a single token.
3492  *
3493  * Do not compensate for the core system's special-case
3494  * adjustment of location to that of the leading '-'
3495  * operator in the event of a negative constant. It is
3496  * also useful for our purposes to start from the minus
3497  * symbol. In this way, queries like "select * from foo
3498  * where bar = 1" and "select * from foo where bar = -2"
3499  * will have identical normalized query strings.
3500  */
3501  tok = core_yylex(&yylval, &yylloc, yyscanner);
3502  if (tok == 0)
3503  break; /* out of inner for-loop */
3504  }
3505 
3506  /*
3507  * We now rely on the assumption that flex has placed a zero
3508  * byte after the text of the current token in scanbuf.
3509  */
3510  locs[i].length = strlen(yyextra.scanbuf + loc);
3511  break; /* out of inner for-loop */
3512  }
3513  }
3514 
3515  /* If we hit end-of-string, give up, leaving remaining lengths -1 */
3516  if (tok == 0)
3517  break;
3518 
3519  last_loc = loc;
3520  }
3521 
3522  scanner_finish(yyscanner);
3523 }
void * core_yyscan_t
Definition: scanner.h:121
PGDLLIMPORT const uint16 ScanKeywordTokens[]
char * scanbuf
Definition: scanner.h:72
PGDLLIMPORT const ScanKeywordList ScanKeywords
pgssLocationLen * clocations
#define YYLTYPE
Definition: scanner.h:44
core_yyscan_t scanner_init(const char *str, core_yy_extra_type *yyext, const ScanKeywordList *keywordlist, const uint16 *keyword_tokens)
static int comp_location(const void *a, const void *b)
int core_yylex(core_YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
#define Assert(condition)
Definition: c.h:804
static core_yyscan_t yyscanner
Definition: pl_scanner.c:106
int i
bool escape_string_warning
Definition: scanner.h:88
void scanner_finish(core_yyscan_t yyscanner)
#define qsort(a, b, c, d)
Definition: port.h:504

◆ gc_qtexts()

static void gc_qtexts ( void  )
static

Definition at line 2341 of file pg_stat_statements.c.

References AllocateFile(), ASSUMED_LENGTH_INIT, DEBUG1, elog, ereport, errcode_for_file_access(), errmsg(), pgssSharedState::extent, free, FreeFile(), ftruncate, hash_seq_init(), hash_seq_search(), hash_seq_term(), LOG, pgssSharedState::mean_query_len, need_gc_qtexts(), PG_BINARY_W, PGSS_TEXT_FILE, qtext_fetch(), qtext_load_file(), pgssEntry::query_len, pgssEntry::query_offset, and record_gc_qtexts.

Referenced by pgss_store().

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

◆ generate_normalized_query()

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

Definition at line 3322 of file pg_stat_statements.c.

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

Referenced by pgss_store().

3324 {
3325  char *norm_query;
3326  int query_len = *query_len_p;
3327  int i,
3328  norm_query_buflen, /* Space allowed for norm_query */
3329  len_to_wrt, /* Length (in bytes) to write */
3330  quer_loc = 0, /* Source query byte location */
3331  n_quer_loc = 0, /* Normalized query byte location */
3332  last_off = 0, /* Offset from start for previous tok */
3333  last_tok_len = 0; /* Length (in bytes) of that tok */
3334 
3335  /*
3336  * Get constants' lengths (core system only gives us locations). Note
3337  * this also ensures the items are sorted by location.
3338  */
3339  fill_in_constant_lengths(jstate, query, query_loc);
3340 
3341  /*
3342  * Allow for $n symbols to be longer than the constants they replace.
3343  * Constants must take at least one byte in text form, while a $n symbol
3344  * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We
3345  * could refine that limit based on the max value of n for the current
3346  * query, but it hardly seems worth any extra effort to do so.
3347  */
3348  norm_query_buflen = query_len + jstate->clocations_count * 10;
3349 
3350  /* Allocate result buffer */
3351  norm_query = palloc(norm_query_buflen + 1);
3352 
3353  for (i = 0; i < jstate->clocations_count; i++)
3354  {
3355  int off, /* Offset from start for cur tok */
3356  tok_len; /* Length (in bytes) of that tok */
3357 
3358  off = jstate->clocations[i].location;
3359  /* Adjust recorded location if we're dealing with partial string */
3360  off -= query_loc;
3361 
3362  tok_len = jstate->clocations[i].length;
3363 
3364  if (tok_len < 0)
3365  continue; /* ignore any duplicates */
3366 
3367  /* Copy next chunk (what precedes the next constant) */
3368  len_to_wrt = off - last_off;
3369  len_to_wrt -= last_tok_len;
3370 
3371  Assert(len_to_wrt >= 0);
3372  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
3373  n_quer_loc += len_to_wrt;
3374 
3375  /* And insert a param symbol in place of the constant token */
3376  n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
3377  i + 1 + jstate->highest_extern_param_id);
3378 
3379  quer_loc = off + tok_len;
3380  last_off = off;
3381  last_tok_len = tok_len;
3382  }
3383 
3384  /*
3385  * We've copied up until the last ignorable constant. Copy over the
3386  * remaining bytes of the original query string.
3387  */
3388  len_to_wrt = query_len - quer_loc;
3389 
3390  Assert(len_to_wrt >= 0);
3391  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
3392  n_quer_loc += len_to_wrt;
3393 
3394  Assert(n_quer_loc <= norm_query_buflen);
3395  norm_query[n_quer_loc] = '\0';
3396 
3397  *query_len_p = n_quer_loc;
3398  return norm_query;
3399 }
#define sprintf
Definition: port.h:218
pgssLocationLen * clocations
static void fill_in_constant_lengths(pgssJumbleState *jstate, const char *query, int query_loc)
#define Assert(condition)
Definition: c.h:804
void * palloc(Size size)
Definition: mcxt.c:950
int i

◆ JumbleExpr()

static void JumbleExpr ( pgssJumbleState jstate,
Node node 
)
static

Definition at line 2802 of file pg_stat_statements.c.

References OnConflictExpr::action, Aggref::aggdirectargs, Aggref::aggdistinct, Aggref::aggfilter, WindowFunc::aggfilter, Aggref::aggfnoid, Aggref::aggorder, SetOperationStmt::all, APP_JUMB, APP_JUMB_STRING, OnConflictExpr::arbiterElems, OnConflictExpr::arbiterWhere, NamedArgExpr::arg, FieldSelect::arg, FieldStore::arg, RelabelType::arg, CoerceViaIO::arg, ArrayCoerceExpr::arg, ConvertRowtypeExpr::arg, CollateExpr::arg, CaseExpr::arg, NullTest::arg, BooleanTest::arg, CoerceToDomain::arg, NamedArgExpr::argnumber, generate_unaccent_rules::args, Aggref::args, WindowFunc::args, FuncExpr::args, OpExpr::args, ScalarArrayOpExpr::args, BoolExpr::args, CaseExpr::args, MinMaxExpr::args, TableSampleClause::args, XmlExpr::args, BoolExpr::boolop, BooleanTest::booltesttype, castNode, check_stack_depth(), TableFunc::colexprs, CollateExpr::collOid, OnConflictExpr::constraint, Const::consttype, GroupingSet::content, CommonTableExpr::ctematerialized, CommonTableExpr::ctename, CommonTableExpr::ctequery, CurrentOfExpr::cursor_name, CurrentOfExpr::cursor_param, CurrentOfExpr::cvarno, CaseExpr::defresult, TableFunc::docexpr, ArrayCoerceExpr::elemexpr, elog, WindowClause::endOffset, SortGroupClause::eqop, OnConflictExpr::exclRelIndex, OnConflictExpr::exclRelTlist, CaseWhen::expr, InferenceElem::expr, TargetEntry::expr, FieldSelect::fieldnum, WindowClause::frameOptions, FromExpr::fromlist, RangeTblFunction::funcexpr, FuncExpr::funcid, pgssJumbleState::highest_extern_param_id, InferenceElem::infercollid, InferenceElem::inferopclass, JoinExpr::isNatural, JoinExpr::jointype, JumbleQuery(), JoinExpr::larg, SetOperationStmt::larg, RowCompareExpr::largs, lfirst, lfirst_int, lfirst_node, Const::location, XmlExpr::named_args, FieldStore::newvals, nodeTag, SortGroupClause::nulls_first, NullTest::nulltesttype, OnConflictExpr::onConflictSet, OnConflictExpr::onConflictWhere, MinMaxExpr::op, SQLValueFunction::op, XmlExpr::op, SetOperationStmt::op, OpExpr::opno, ScalarArrayOpExpr::opno, WindowClause::orderClause, PARAM_EXTERN, Param::paramid, Param::paramkind, Param::paramtype, WindowClause::partitionClause, JoinExpr::quals, FromExpr::quals, JoinExpr::rarg, SetOperationStmt::rarg, RowCompareExpr::rargs, RowCompareExpr::rctype, RecordConstLocation(), SubscriptingRef::refassgnexpr, SubscriptingRef::refexpr, SubscriptingRef::reflowerindexpr, GroupingFunc::refs, SubscriptingRef::refupperindexpr, TableSampleClause::repeatable, TargetEntry::resno, TargetEntry::ressortgroupref, CaseWhen::result, RelabelType::resulttype, CoerceViaIO::resulttype, ArrayCoerceExpr::resulttype, ConvertRowtypeExpr::resulttype, CoerceToDomain::resulttype, TableFunc::rowexpr, RangeTblRef::rtindex, JoinExpr::rtindex, NextValueExpr::seqid, SortGroupClause::sortop, WindowClause::startOffset, SubLink::subLinkId, SubLink::subLinkType, SubLink::subselect, T_Aggref, T_ArrayCoerceExpr, T_ArrayExpr, T_BooleanTest, T_BoolExpr, T_CaseExpr, T_CaseTestExpr, T_CoalesceExpr, T_CoerceToDomain, T_CoerceToDomainValue, T_CoerceViaIO, T_CollateExpr, T_CommonTableExpr, T_Const, T_ConvertRowtypeExpr, T_CurrentOfExpr, T_DistinctExpr, T_FieldSelect, T_FieldStore, T_FromExpr, T_FuncExpr, T_GroupingFunc, T_GroupingSet, T_InferenceElem, T_IntList, T_JoinExpr, T_List, T_MinMaxExpr, T_NamedArgExpr, T_NextValueExpr, T_NullIfExpr, T_NullTest, T_OnConflictExpr, T_OpExpr, T_Param, T_RangeTblFunction, T_RangeTblRef, T_RelabelType, T_RowCompareExpr, T_RowExpr, T_ScalarArrayOpExpr, T_SetOperationStmt, T_SetToDefault, T_SortGroupClause, T_SQLValueFunction, T_SubLink, T_SubscriptingRef, T_TableFunc, T_TableSampleClause, T_TargetEntry, T_Var, T_WindowClause, T_WindowFunc, T_XmlExpr, SubLink::testexpr, SortGroupClause::tleSortGroupRef, TableSampleClause::tsmhandler, Node::type, CaseTestExpr::typeId, CoerceToDomainValue::typeId, SetToDefault::typeId, NextValueExpr::typeId, SQLValueFunction::typmod, ScalarArrayOpExpr::useOr, Var::varattno, Var::varlevelsup, Var::varno, WARNING, WindowFunc::winfnoid, WindowFunc::winref, and WindowClause::winref.

Referenced by JumbleQuery(), and JumbleRangeTable().

2803 {
2804  ListCell *temp;
2805 
2806  if (node == NULL)
2807  return;
2808 
2809  /* Guard against stack overflow due to overly complex expressions */
2811 
2812  /*
2813  * We always emit the node's NodeTag, then any additional fields that are
2814  * considered significant, and then we recurse to any child nodes.
2815  */
2816  APP_JUMB(node->type);
2817 
2818  switch (nodeTag(node))
2819  {
2820  case T_Var:
2821  {
2822  Var *var = (Var *) node;
2823 
2824  APP_JUMB(var->varno);
2825  APP_JUMB(var->varattno);
2826  APP_JUMB(var->varlevelsup);
2827  }
2828  break;
2829  case T_Const:
2830  {
2831  Const *c = (Const *) node;
2832 
2833  /* We jumble only the constant's type, not its value */
2834  APP_JUMB(c->consttype);
2835  /* Also, record its parse location for query normalization */
2836  RecordConstLocation(jstate, c->location);
2837  }
2838  break;
2839  case T_Param:
2840  {
2841  Param *p = (Param *) node;
2842 
2843  APP_JUMB(p->paramkind);
2844  APP_JUMB(p->paramid);
2845  APP_JUMB(p->paramtype);
2846  /* Also, track the highest external Param id */
2847  if (p->paramkind == PARAM_EXTERN &&
2848  p->paramid > jstate->highest_extern_param_id)
2849  jstate->highest_extern_param_id = p->paramid;
2850  }
2851  break;
2852  case T_Aggref:
2853  {
2854  Aggref *expr = (Aggref *) node;
2855 
2856  APP_JUMB(expr->aggfnoid);
2857  JumbleExpr(jstate, (Node *) expr->aggdirectargs);
2858  JumbleExpr(jstate, (Node *) expr->args);
2859  JumbleExpr(jstate, (Node *) expr->aggorder);
2860  JumbleExpr(jstate, (Node *) expr->aggdistinct);
2861  JumbleExpr(jstate, (Node *) expr->aggfilter);
2862  }
2863  break;
2864  case T_GroupingFunc:
2865  {
2866  GroupingFunc *grpnode = (GroupingFunc *) node;
2867 
2868  JumbleExpr(jstate, (Node *) grpnode->refs);
2869  }
2870  break;
2871  case T_WindowFunc:
2872  {
2873  WindowFunc *expr = (WindowFunc *) node;
2874 
2875  APP_JUMB(expr->winfnoid);
2876  APP_JUMB(expr->winref);
2877  JumbleExpr(jstate, (Node *) expr->args);
2878  JumbleExpr(jstate, (Node *) expr->aggfilter);
2879  }
2880  break;
2881  case T_SubscriptingRef:
2882  {
2883  SubscriptingRef *sbsref = (SubscriptingRef *) node;
2884 
2885  JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
2886  JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
2887  JumbleExpr(jstate, (Node *) sbsref->refexpr);
2888  JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
2889  }
2890  break;
2891  case T_FuncExpr:
2892  {
2893  FuncExpr *expr = (FuncExpr *) node;
2894 
2895  APP_JUMB(expr->funcid);
2896  JumbleExpr(jstate, (Node *) expr->args);
2897  }
2898  break;
2899  case T_NamedArgExpr:
2900  {
2901  NamedArgExpr *nae = (NamedArgExpr *) node;
2902 
2903  APP_JUMB(nae->argnumber);
2904  JumbleExpr(jstate, (Node *) nae->arg);
2905  }
2906  break;
2907  case T_OpExpr:
2908  case T_DistinctExpr: /* struct-equivalent to OpExpr */
2909  case T_NullIfExpr: /* struct-equivalent to OpExpr */
2910  {
2911  OpExpr *expr = (OpExpr *) node;
2912 
2913  APP_JUMB(expr->opno);
2914  JumbleExpr(jstate, (Node *) expr->args);
2915  }
2916  break;
2917  case T_ScalarArrayOpExpr:
2918  {
2919  ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
2920 
2921  APP_JUMB(expr->opno);
2922  APP_JUMB(expr->useOr);
2923  JumbleExpr(jstate, (Node *) expr->args);
2924  }
2925  break;
2926  case T_BoolExpr:
2927  {
2928  BoolExpr *expr = (BoolExpr *) node;
2929 
2930  APP_JUMB(expr->boolop);
2931  JumbleExpr(jstate, (Node *) expr->args);
2932  }
2933  break;
2934  case T_SubLink:
2935  {
2936  SubLink *sublink = (SubLink *) node;
2937 
2938  APP_JUMB(sublink->subLinkType);
2939  APP_JUMB(sublink->subLinkId);
2940  JumbleExpr(jstate, (Node *) sublink->testexpr);
2941  JumbleQuery(jstate, castNode(Query, sublink->subselect));
2942  }
2943  break;
2944  case T_FieldSelect:
2945  {
2946  FieldSelect *fs = (FieldSelect *) node;
2947 
2948  APP_JUMB(fs->fieldnum);
2949  JumbleExpr(jstate, (Node *) fs->arg);
2950  }
2951  break;
2952  case T_FieldStore:
2953  {
2954  FieldStore *fstore = (FieldStore *) node;
2955 
2956  JumbleExpr(jstate, (Node *) fstore->arg);
2957  JumbleExpr(jstate, (Node *) fstore->newvals);
2958  }
2959  break;
2960  case T_RelabelType:
2961  {
2962  RelabelType *rt = (RelabelType *) node;
2963 
2964  APP_JUMB(rt->resulttype);
2965  JumbleExpr(jstate, (Node *) rt->arg);
2966  }
2967  break;
2968  case T_CoerceViaIO:
2969  {
2970  CoerceViaIO *cio = (CoerceViaIO *) node;
2971 
2972  APP_JUMB(cio->resulttype);
2973  JumbleExpr(jstate, (Node *) cio->arg);
2974  }
2975  break;
2976  case T_ArrayCoerceExpr:
2977  {
2978  ArrayCoerceExpr *acexpr = (ArrayCoerceExpr *) node;
2979 
2980  APP_JUMB(acexpr->resulttype);
2981  JumbleExpr(jstate, (Node *) acexpr->arg);
2982  JumbleExpr(jstate, (Node *) acexpr->elemexpr);
2983  }
2984  break;
2985  case T_ConvertRowtypeExpr:
2986  {
2987  ConvertRowtypeExpr *crexpr = (ConvertRowtypeExpr *) node;
2988 
2989  APP_JUMB(crexpr->resulttype);
2990  JumbleExpr(jstate, (Node *) crexpr->arg);
2991  }
2992  break;
2993  case T_CollateExpr:
2994  {
2995  CollateExpr *ce = (CollateExpr *) node;
2996 
2997  APP_JUMB(ce->collOid);
2998  JumbleExpr(jstate, (Node *) ce->arg);
2999  }
3000  break;
3001  case T_CaseExpr:
3002  {
3003  CaseExpr *caseexpr = (CaseExpr *) node;
3004 
3005  JumbleExpr(jstate, (Node *) caseexpr->arg);
3006  foreach(temp, caseexpr->args)
3007  {
3008  CaseWhen *when = lfirst_node(CaseWhen, temp);
3009 
3010  JumbleExpr(jstate, (Node *) when->expr);
3011  JumbleExpr(jstate, (Node *) when->result);
3012  }
3013  JumbleExpr(jstate, (Node *) caseexpr->defresult);
3014  }
3015  break;
3016  case T_CaseTestExpr:
3017  {
3018  CaseTestExpr *ct = (CaseTestExpr *) node;
3019 
3020  APP_JUMB(ct->typeId);
3021  }
3022  break;
3023  case T_ArrayExpr:
3024  JumbleExpr(jstate, (Node *) ((ArrayExpr *) node)->elements);
3025  break;
3026  case T_RowExpr:
3027  JumbleExpr(jstate, (Node *) ((RowExpr *) node)->args);
3028  break;
3029  case T_RowCompareExpr:
3030  {
3031  RowCompareExpr *rcexpr = (RowCompareExpr *) node;
3032 
3033  APP_JUMB(rcexpr->rctype);
3034  JumbleExpr(jstate, (Node *) rcexpr->largs);
3035  JumbleExpr(jstate, (Node *) rcexpr->rargs);
3036  }
3037  break;
3038  case T_CoalesceExpr:
3039  JumbleExpr(jstate, (Node *) ((CoalesceExpr *) node)->args);
3040  break;
3041  case T_MinMaxExpr:
3042  {
3043  MinMaxExpr *mmexpr = (MinMaxExpr *) node;
3044 
3045  APP_JUMB(mmexpr->op);
3046  JumbleExpr(jstate, (Node *) mmexpr->args);
3047  }
3048  break;
3049  case T_SQLValueFunction:
3050  {
3051  SQLValueFunction *svf = (SQLValueFunction *) node;
3052 
3053  APP_JUMB(svf->op);
3054  /* type is fully determined by op */
3055  APP_JUMB(svf->typmod);
3056  }
3057  break;
3058  case T_XmlExpr:
3059  {
3060  XmlExpr *xexpr = (XmlExpr *) node;
3061 
3062  APP_JUMB(xexpr->op);
3063  JumbleExpr(jstate, (Node *) xexpr->named_args);
3064  JumbleExpr(jstate, (Node *) xexpr->args);
3065  }
3066  break;
3067  case T_NullTest:
3068  {
3069  NullTest *nt = (NullTest *) node;
3070 
3071  APP_JUMB(nt->nulltesttype);
3072  JumbleExpr(jstate, (Node *) nt->arg);
3073  }
3074  break;
3075  case T_BooleanTest:
3076  {
3077  BooleanTest *bt = (BooleanTest *) node;
3078 
3079  APP_JUMB(bt->booltesttype);
3080  JumbleExpr(jstate, (Node *) bt->arg);
3081  }
3082  break;
3083  case T_CoerceToDomain:
3084  {
3085  CoerceToDomain *cd = (CoerceToDomain *) node;
3086 
3087  APP_JUMB(cd->resulttype);
3088  JumbleExpr(jstate, (Node *) cd->arg);
3089  }
3090  break;
3091  case T_CoerceToDomainValue:
3092  {
3093  CoerceToDomainValue *cdv = (CoerceToDomainValue *) node;
3094 
3095  APP_JUMB(cdv->typeId);
3096  }
3097  break;
3098  case T_SetToDefault:
3099  {
3100  SetToDefault *sd = (SetToDefault *) node;
3101 
3102  APP_JUMB(sd->typeId);
3103  }
3104  break;
3105  case T_CurrentOfExpr:
3106  {
3107  CurrentOfExpr *ce = (CurrentOfExpr *) node;
3108 
3109  APP_JUMB(ce->cvarno);
3110  if (ce->cursor_name)
3112  APP_JUMB(ce->cursor_param);
3113  }
3114  break;
3115  case T_NextValueExpr:
3116  {
3117  NextValueExpr *nve = (NextValueExpr *) node;
3118 
3119  APP_JUMB(nve->seqid);
3120  APP_JUMB(nve->typeId);
3121  }
3122  break;
3123  case T_InferenceElem:
3124  {
3125  InferenceElem *ie = (InferenceElem *) node;
3126 
3127  APP_JUMB(ie->infercollid);
3128  APP_JUMB(ie->inferopclass);
3129  JumbleExpr(jstate, ie->expr);
3130  }
3131  break;
3132  case T_TargetEntry:
3133  {
3134  TargetEntry *tle = (TargetEntry *) node;
3135 
3136  APP_JUMB(tle->resno);
3137  APP_JUMB(tle->ressortgroupref);
3138  JumbleExpr(jstate, (Node *) tle->expr);
3139  }
3140  break;
3141  case T_RangeTblRef:
3142  {
3143  RangeTblRef *rtr = (RangeTblRef *) node;
3144 
3145  APP_JUMB(rtr->rtindex);
3146  }
3147  break;
3148  case T_JoinExpr:
3149  {
3150  JoinExpr *join = (JoinExpr *) node;
3151 
3152  APP_JUMB(join->jointype);
3153  APP_JUMB(join->isNatural);
3154  APP_JUMB(join->rtindex);
3155  JumbleExpr(jstate, join->larg);
3156  JumbleExpr(jstate, join->rarg);
3157  JumbleExpr(jstate, join->quals);
3158  }
3159  break;
3160  case T_FromExpr:
3161  {
3162  FromExpr *from = (FromExpr *) node;
3163 
3164  JumbleExpr(jstate, (Node *) from->fromlist);
3165  JumbleExpr(jstate, from->quals);
3166  }
3167  break;
3168  case T_OnConflictExpr:
3169  {
3170  OnConflictExpr *conf = (OnConflictExpr *) node;
3171 
3172  APP_JUMB(conf->action);
3173  JumbleExpr(jstate, (Node *) conf->arbiterElems);
3174  JumbleExpr(jstate, conf->arbiterWhere);
3175  JumbleExpr(jstate, (Node *) conf->onConflictSet);
3176  JumbleExpr(jstate, conf->onConflictWhere);
3177  APP_JUMB(conf->constraint);
3178  APP_JUMB(conf->exclRelIndex);
3179  JumbleExpr(jstate, (Node *) conf->exclRelTlist);
3180  }
3181  break;
3182  case T_List:
3183  foreach(temp, (List *) node)
3184  {
3185  JumbleExpr(jstate, (Node *) lfirst(temp));
3186  }
3187  break;
3188  case T_IntList:
3189  foreach(temp, (List *) node)
3190  {
3191  APP_JUMB(lfirst_int(temp));
3192  }
3193  break;
3194  case T_SortGroupClause:
3195  {
3196  SortGroupClause *sgc = (SortGroupClause *) node;
3197 
3198  APP_JUMB(sgc->tleSortGroupRef);
3199  APP_JUMB(sgc->eqop);
3200  APP_JUMB(sgc->sortop);
3201  APP_JUMB(sgc->nulls_first);
3202  }
3203  break;
3204  case T_GroupingSet:
3205  {
3206  GroupingSet *gsnode = (GroupingSet *) node;
3207 
3208  JumbleExpr(jstate, (Node *) gsnode->content);
3209  }
3210  break;
3211  case T_WindowClause:
3212  {
3213  WindowClause *wc = (WindowClause *) node;
3214 
3215  APP_JUMB(wc->winref);
3216  APP_JUMB(wc->frameOptions);
3217  JumbleExpr(jstate, (Node *) wc->partitionClause);
3218  JumbleExpr(jstate, (Node *) wc->orderClause);
3219  JumbleExpr(jstate, wc->startOffset);
3220  JumbleExpr(jstate, wc->endOffset);
3221  }
3222  break;
3223  case T_CommonTableExpr:
3224  {
3225  CommonTableExpr *cte = (CommonTableExpr *) node;
3226 
3227  /* we store the string name because RTE_CTE RTEs need it */
3228  APP_JUMB_STRING(cte->ctename);
3229  APP_JUMB(cte->ctematerialized);
3230  JumbleQuery(jstate, castNode(Query, cte->ctequery));
3231  }
3232  break;
3233  case T_SetOperationStmt:
3234  {
3235  SetOperationStmt *setop = (SetOperationStmt *) node;
3236 
3237  APP_JUMB(setop->op);
3238  APP_JUMB(setop->all);
3239  JumbleExpr(jstate, setop->larg);
3240  JumbleExpr(jstate, setop->rarg);
3241  }
3242  break;
3243  case T_RangeTblFunction:
3244  {
3245  RangeTblFunction *rtfunc = (RangeTblFunction *) node;
3246 
3247  JumbleExpr(jstate, rtfunc->funcexpr);
3248  }
3249  break;
3250  case T_TableFunc:
3251  {
3252  TableFunc *tablefunc = (TableFunc *) node;
3253 
3254  JumbleExpr(jstate, tablefunc->docexpr);
3255  JumbleExpr(jstate, tablefunc->rowexpr);
3256  JumbleExpr(jstate, (Node *) tablefunc->colexprs);
3257  }
3258  break;
3259  case T_TableSampleClause:
3260  {
3261  TableSampleClause *tsc = (TableSampleClause *) node;
3262 
3263  APP_JUMB(tsc->tsmhandler);
3264  JumbleExpr(jstate, (Node *) tsc->args);
3265  JumbleExpr(jstate, (Node *) tsc->repeatable);
3266  }
3267  break;
3268  default:
3269  /* Only a warning, since we can stumble along anyway */
3270  elog(WARNING, "unrecognized node type: %d",
3271  (int) nodeTag(node));
3272  break;
3273  }
3274 }
List * aggdistinct
Definition: primnodes.h:327
List * args
Definition: primnodes.h:1130
static void JumbleExpr(pgssJumbleState *jstate, Node *node)
Node * docexpr
Definition: primnodes.h:87
Expr * arg
Definition: primnodes.h:815
Index varlevelsup
Definition: primnodes.h:191
List * content
Definition: parsenodes.h:1332
List * refs
Definition: primnodes.h:369
List * args
Definition: primnodes.h:385
List * args
Definition: primnodes.h:498
#define castNode(_type_, nodeptr)
Definition: nodes.h:602
Oid resulttype
Definition: primnodes.h:859
RowCompareType rctype
Definition: primnodes.h:1094
Index tleSortGroupRef
Definition: parsenodes.h:1262
Expr * arg
Definition: primnodes.h:838
ParamKind paramkind
Definition: primnodes.h:262
Definition: nodes.h:533
List * args
Definition: primnodes.h:325
AttrNumber varattno
Definition: primnodes.h:186
Expr * arg
Definition: primnodes.h:786
List * fromlist
Definition: primnodes.h:1534
Index winref
Definition: primnodes.h:387
Definition: primnodes.h:181
List * refupperindexpr
Definition: primnodes.h:439
Node * quals
Definition: primnodes.h:1535
SQLValueFunctionOp op
Definition: primnodes.h:1167
#define APP_JUMB_STRING(str)
static void RecordConstLocation(pgssJumbleState *jstate, int location)
List * arbiterElems
Definition: primnodes.h:1553
Node * larg
Definition: primnodes.h:1514
Oid consttype
Definition: primnodes.h:210
Oid funcid
Definition: primnodes.h:490
#define lfirst_int(lc)
Definition: pg_list.h:170
List * partitionClause
Definition: parsenodes.h:1358
BoolExprType boolop
Definition: primnodes.h:603
Expr * arg
Definition: primnodes.h:1243
#define lfirst_node(type, lc)
Definition: pg_list.h:172
#define APP_JUMB(item)
Node * rowexpr
Definition: primnodes.h:88
char * c
NodeTag type
Definition: nodes.h:535
static void JumbleQuery(pgssJumbleState *jstate, Query *query)
List * exclRelTlist
Definition: primnodes.h:1562
void check_stack_depth(void)
Definition: postgres.c:3310
List * aggorder
Definition: primnodes.h:326
Expr * arg
Definition: primnodes.h:1266
AttrNumber resno
Definition: primnodes.h:1432
char * cursor_name
Definition: primnodes.h:1341
List * aggdirectargs
Definition: primnodes.h:324
Oid winfnoid
Definition: primnodes.h:381
Expr * arg
Definition: primnodes.h:858
Expr * elemexpr
Definition: primnodes.h:883
Definition: type.h:82
Definition: nodes.h:301
List * newvals
Definition: primnodes.h:816
Definition: nodes.h:156
OnConflictAction action
Definition: primnodes.h:1550
bool isNatural
Definition: primnodes.h:1513
#define WARNING
Definition: elog.h:40
Index varno
Definition: primnodes.h:184
Definition: nodes.h:155
XmlExprOp op
Definition: primnodes.h:1205
Node * startOffset
Definition: parsenodes.h:1361
List * args
Definition: primnodes.h:957
int location
Definition: primnodes.h:221
Node * quals
Definition: primnodes.h:1517
BoolTestType booltesttype
Definition: primnodes.h:1267
Oid resulttype
Definition: primnodes.h:839
NullTestType nulltesttype
Definition: primnodes.h:1244
Oid aggfnoid
Definition: primnodes.h:318
List * colexprs
Definition: primnodes.h:93
List * named_args
Definition: primnodes.h:1207
List * args
Definition: primnodes.h:1209
Node * rarg
Definition: primnodes.h:1515
Expr * arg
Definition: primnodes.h:519
JoinType jointype
Definition: primnodes.h:1512
#define lfirst(lc)
Definition: pg_list.h:169
CTEMaterialize ctematerialized
Definition: parsenodes.h:1481
Expr * aggfilter
Definition: primnodes.h:386
Expr * expr
Definition: primnodes.h:1431
int paramid
Definition: primnodes.h:263
Node * endOffset
Definition: parsenodes.h:1362
Expr * arg
Definition: primnodes.h:924
Expr * aggfilter
Definition: primnodes.h:328
SetOperation op
Definition: parsenodes.h:1689
List * args
Definition: primnodes.h:604
#define nodeTag(nodeptr)
Definition: nodes.h:538
List * orderClause
Definition: parsenodes.h:1359
Node * arbiterWhere
Definition: primnodes.h:1555
Expr * refassgnexpr
Definition: primnodes.h:446
List * reflowerindexpr
Definition: primnodes.h:441
#define elog(elevel,...)
Definition: elog.h:227
List * onConflictSet
Definition: primnodes.h:1559
Index ressortgroupref
Definition: primnodes.h:1434
MinMaxOp op
Definition: primnodes.h:1129
Expr * refexpr
Definition: primnodes.h:444
Expr * arg
Definition: primnodes.h:956
Oid opno
Definition: primnodes.h:537
Expr * result
Definition: primnodes.h:969
List * args
Definition: primnodes.h:543
Expr * defresult
Definition: primnodes.h:958
Expr * expr
Definition: primnodes.h:968
Node * onConflictWhere
Definition: primnodes.h:1560
int rtindex
Definition: primnodes.h:1519
Definition: pg_list.h:50
Oid paramtype
Definition: primnodes.h:264
Definition: nodes.h:157
AttrNumber fieldnum
Definition: primnodes.h:787

◆ JumbleQuery()

static void JumbleQuery ( pgssJumbleState jstate,
Query query 
)
static

Definition at line 2686 of file pg_stat_statements.c.

References APP_JUMB, Assert, Query::commandType, Query::cteList, Query::distinctClause, Query::groupClause, Query::groupingSets, Query::havingQual, IsA, Query::jointree, JumbleExpr(), JumbleRangeTable(), JumbleRowMarks(), Query::limitCount, Query::limitOffset, Query::onConflict, Query::returningList, Query::rowMarks, Query::rtable, Query::setOperations, Query::sortClause, Query::targetList, Query::utilityStmt, and Query::windowClause.

Referenced by JumbleExpr(), JumbleRangeTable(), and pgss_post_parse_analyze().

2687 {
2688  Assert(IsA(query, Query));
2689  Assert(query->utilityStmt == NULL);
2690 
2691  APP_JUMB(query->commandType);
2692  /* resultRelation is usually predictable from commandType */
2693  JumbleExpr(jstate, (Node *) query->cteList);
2694  JumbleRangeTable(jstate, query->rtable);
2695  JumbleExpr(jstate, (Node *) query->jointree);
2696  JumbleExpr(jstate, (Node *) query->targetList);
2697  JumbleExpr(jstate, (Node *) query->onConflict);
2698  JumbleExpr(jstate, (Node *) query->returningList);
2699  JumbleExpr(jstate, (Node *) query->groupClause);
2700  JumbleExpr(jstate, (Node *) query->groupingSets);
2701  JumbleExpr(jstate, query->havingQual);
2702  JumbleExpr(jstate, (Node *) query->windowClause);
2703  JumbleExpr(jstate, (Node *) query->distinctClause);
2704  JumbleExpr(jstate, (Node *) query->sortClause);
2705  JumbleExpr(jstate, query->limitOffset);
2706  JumbleExpr(jstate, query->limitCount);
2707  JumbleRowMarks(jstate, query->rowMarks);
2708  JumbleExpr(jstate, query->setOperations);
2709 }
Node * limitOffset
Definition: parsenodes.h:160
static void JumbleExpr(pgssJumbleState *jstate, Node *node)
#define IsA(nodeptr, _type_)
Definition: nodes.h:584
List * sortClause
Definition: parsenodes.h:158
static void JumbleRowMarks(pgssJumbleState *jstate, List *rowMarks)
FromExpr * jointree
Definition: parsenodes.h:138
OnConflictExpr * onConflict
Definition: parsenodes.h:144
List * groupingSets
Definition: parsenodes.h:150
Definition: nodes.h:533
List * rowMarks
Definition: parsenodes.h:164
Node * utilityStmt
Definition: parsenodes.h:120
List * windowClause
Definition: parsenodes.h:154
List * targetList
Definition: parsenodes.h:140
static void JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
List * rtable
Definition: parsenodes.h:137
List * distinctClause
Definition: parsenodes.h:156
#define APP_JUMB(item)
Node * limitCount
Definition: parsenodes.h:161
List * returningList
Definition: parsenodes.h:146
CmdType commandType
Definition: parsenodes.h:112
#define Assert(condition)
Definition: c.h:804
List * cteList
Definition: parsenodes.h:135
Node * setOperations
Definition: parsenodes.h:166
List * groupClause
Definition: parsenodes.h:148
Node * havingQual
Definition: parsenodes.h:152

◆ JumbleRangeTable()

static void JumbleRangeTable ( pgssJumbleState jstate,
List rtable 
)
static

Definition at line 2715 of file pg_stat_statements.c.

References APP_JUMB, APP_JUMB_STRING, RangeTblEntry::ctelevelsup, RangeTblEntry::ctename, elog, RangeTblEntry::enrname, ERROR, RangeTblEntry::functions, RangeTblEntry::jointype, JumbleExpr(), JumbleQuery(), lfirst_node, RangeTblEntry::relid, RTE_CTE, RTE_FUNCTION, RTE_JOIN, RTE_NAMEDTUPLESTORE, RTE_RELATION, RTE_RESULT, RTE_SUBQUERY, RTE_TABLEFUNC, RTE_VALUES, RangeTblEntry::rtekind, RangeTblEntry::subquery, RangeTblEntry::tablefunc, RangeTblEntry::tablesample, and RangeTblEntry::values_lists.

Referenced by JumbleQuery().

2716 {
2717  ListCell *lc;
2718 
2719  foreach(lc, rtable)
2720  {
2722 
2723  APP_JUMB(rte->rtekind);
2724  switch (rte->rtekind)
2725  {
2726  case RTE_RELATION:
2727  APP_JUMB(rte->relid);
2728  JumbleExpr(jstate, (Node *) rte->tablesample);
2729  break;
2730  case RTE_SUBQUERY:
2731  JumbleQuery(jstate, rte->subquery);
2732  break;
2733  case RTE_JOIN:
2734  APP_JUMB(rte->jointype);
2735  break;
2736  case RTE_FUNCTION:
2737  JumbleExpr(jstate, (Node *) rte->functions);
2738  break;
2739  case RTE_TABLEFUNC:
2740  JumbleExpr(jstate, (Node *) rte->tablefunc);
2741  break;
2742  case RTE_VALUES:
2743  JumbleExpr(jstate, (Node *) rte->values_lists);
2744  break;
2745  case RTE_CTE:
2746 
2747  /*
2748  * Depending on the CTE name here isn't ideal, but it's the
2749  * only info we have to identify the referenced WITH item.
2750  */
2751  APP_JUMB_STRING(rte->ctename);
2752  APP_JUMB(rte->ctelevelsup);
2753  break;
2754  case RTE_NAMEDTUPLESTORE:
2755  APP_JUMB_STRING(rte->enrname);
2756  break;
2757  case RTE_RESULT:
2758  break;
2759  default:
2760  elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
2761  break;
2762  }
2763  }
2764 }
static void JumbleExpr(pgssJumbleState *jstate, Node *node)
Definition: nodes.h:533
List * values_lists
Definition: parsenodes.h:1079
#define APP_JUMB_STRING(str)
#define ERROR
Definition: elog.h:45
TableFunc * tablefunc
Definition: parsenodes.h:1074
#define lfirst_node(type, lc)
Definition: pg_list.h:172
#define APP_JUMB(item)
static void JumbleQuery(pgssJumbleState *jstate, Query *query)
JoinType jointype
Definition: parsenodes.h:1054
char * enrname
Definition: parsenodes.h:1113
List * functions
Definition: parsenodes.h:1068
Index ctelevelsup
Definition: parsenodes.h:1085
RTEKind rtekind
Definition: parsenodes.h:981
char * ctename
Definition: parsenodes.h:1084
Query * subquery
Definition: parsenodes.h:1016
#define elog(elevel,...)
Definition: elog.h:227
struct TableSampleClause * tablesample
Definition: parsenodes.h:1011

◆ JumbleRowMarks()

static void JumbleRowMarks ( pgssJumbleState jstate,
List rowMarks 
)
static

Definition at line 2770 of file pg_stat_statements.c.

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

Referenced by JumbleQuery().

2771 {
2772  ListCell *lc;
2773 
2774  foreach(lc, rowMarks)
2775  {
2776  RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
2777 
2778  if (!rowmark->pushedDown)
2779  {
2780  APP_JUMB(rowmark->rti);
2781  APP_JUMB(rowmark->strength);
2782  APP_JUMB(rowmark->waitPolicy);
2783  }
2784  }
2785 }
LockClauseStrength strength
Definition: parsenodes.h:1388
#define lfirst_node(type, lc)
Definition: pg_list.h:172
#define APP_JUMB(item)
LockWaitPolicy waitPolicy
Definition: parsenodes.h:1389

◆ need_gc_qtexts()

static bool need_gc_qtexts ( void  )
static

Definition at line 2294 of file pg_stat_statements.c.

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

Referenced by gc_qtexts(), and pgss_store().

2295 {
2296  Size extent;
2297 
2298  /* Read shared extent pointer */
2299  {
2300  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2301 
2302  SpinLockAcquire(&s->mutex);
2303  extent = s->extent;
2304  SpinLockRelease(&s->mutex);
2305  }
2306 
2307  /* Don't proceed if file does not exceed 512 bytes per possible entry */
2308  if (extent < 512 * pgss_max)
2309  return false;
2310 
2311  /*
2312  * Don't proceed if file is less than about 50% bloat. Nothing can or
2313  * should be done in the event of unusually large query texts accounting
2314  * for file's large size. We go to the trouble of maintaining the mean
2315  * query length in order to prevent garbage collection from thrashing
2316  * uselessly.
2317  */
2318  if (extent < pgss->mean_query_len * pgss_max * 2)
2319  return false;
2320 
2321  return true;
2322 }
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
static int pgss_max
#define SpinLockRelease(lock)
Definition: spin.h:64
size_t Size
Definition: c.h:540

◆ PG_FUNCTION_INFO_V1() [1/7]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_reset  )

◆ PG_FUNCTION_INFO_V1() [2/7]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_reset_1_7  )

◆ PG_FUNCTION_INFO_V1() [3/7]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_2  )

◆ PG_FUNCTION_INFO_V1() [4/7]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_3  )

◆ PG_FUNCTION_INFO_V1() [5/7]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_8  )

◆ PG_FUNCTION_INFO_V1() [6/7]

PG_FUNCTION_INFO_V1 ( pg_stat_statements  )

◆ PG_FUNCTION_INFO_V1() [7/7]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_info  )

◆ pg_stat_statements()

Datum pg_stat_statements ( PG_FUNCTION_ARGS  )

Definition at line 1561 of file pg_stat_statements.c.

References pg_stat_statements_internal(), and PGSS_V1_0.

1562 {
1563  /* If it's really API 1.1, we'll figure that out below */
1564  pg_stat_statements_internal(fcinfo, PGSS_V1_0, true);
1565 
1566  return (Datum) 0;
1567 }
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:367

◆ pg_stat_statements_1_2()

Datum pg_stat_statements_1_2 ( PG_FUNCTION_ARGS  )

Definition at line 1547 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_2.

1548 {
1549  bool showtext = PG_GETARG_BOOL(0);
1550 
1551  pg_stat_statements_internal(fcinfo, PGSS_V1_2, showtext);
1552 
1553  return (Datum) 0;
1554 }
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:367

◆ pg_stat_statements_1_3()

Datum pg_stat_statements_1_3 ( PG_FUNCTION_ARGS  )

Definition at line 1537 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_3.

1538 {
1539  bool showtext = PG_GETARG_BOOL(0);
1540 
1541  pg_stat_statements_internal(fcinfo, PGSS_V1_3, showtext);
1542 
1543  return (Datum) 0;
1544 }
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:367

◆ pg_stat_statements_1_8()

Datum pg_stat_statements_1_8 ( PG_FUNCTION_ARGS  )

Definition at line 1527 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_8.

1528 {
1529  bool showtext = PG_GETARG_BOOL(0);
1530 
1531  pg_stat_statements_internal(fcinfo, PGSS_V1_8, showtext);
1532 
1533  return (Datum) 0;
1534 }
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:367

◆ pg_stat_statements_info()

Datum pg_stat_statements_info ( PG_FUNCTION_ARGS  )

Definition at line 1894 of file pg_stat_statements.c.

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

1895 {
1896  pgssGlobalStats stats;
1897  TupleDesc tupdesc;
1899  bool nulls[PG_STAT_STATEMENTS_INFO_COLS];
1900 
1901  if (!pgss || !pgss_hash)
1902  ereport(ERROR,
1903  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1904  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
1905 
1906  /* Build a tuple descriptor for our result type */
1907  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1908  elog(ERROR, "return type must be a row type");
1909 
1910  MemSet(values, 0, sizeof(values));
1911  MemSet(nulls, 0, sizeof(nulls));
1912 
1913  /* Read global statistics for pg_stat_statements */
1914  {
1915  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1916 
1917  SpinLockAcquire(&s->mutex);
1918  stats = s->stats;
1919  SpinLockRelease(&s->mutex);
1920  }
1921 
1922  values[0] = Int64GetDatum(stats.dealloc);
1923  values[1] = TimestampTzGetDatum(stats.stats_reset);
1924 
1925  PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
1926 }
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:207
#define PG_STAT_STATEMENTS_INFO_COLS
int errcode(int sqlerrcode)
Definition: elog.c:694
#define MemSet(start, val, len)
Definition: c.h:1008
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
static HTAB * pgss_hash
#define ERROR
Definition: elog.h:45
#define TimestampTzGetDatum(X)
Definition: timestamp.h:32
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1700
pgssGlobalStats stats
#define SpinLockRelease(lock)
Definition: spin.h:64
uintptr_t Datum
Definition: postgres.h:367
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define ereport(elevel,...)
Definition: elog.h:155
TimestampTz stats_reset
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:221
static Datum values[MAXATTR]
Definition: bootstrap.c:165
int errmsg(const char *fmt,...)
Definition: elog.c:905
#define elog(elevel,...)
Definition: elog.h:227

◆ pg_stat_statements_internal()

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

Definition at line 1571 of file pg_stat_statements.c.

References ReturnSetInfo::allowedModes, Assert, Counters::blk_read_time, Counters::blk_write_time, buf, Counters::calls, pgssEntry::counters, CStringGetDatum, CStringGetTextDatum, pgssHashKey::dbid, DirectFunctionCall3, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, elog, enc, pgssEntry::encoding, ereport, errcode(), errmsg(), ERROR, pgssSharedState::extent, Float8GetDatumFast, free, pgssSharedState::gc_count, get_call_result_type(), GetUserId(), hash_seq_init(), hash_seq_search(), i, Int32GetDatum, Int64GetDatumFast, is_member_of_role(), IS_STICKY, IsA, pgssEntry::key, Counters::local_blks_dirtied, Counters::local_blks_hit, Counters::local_blks_read, Counters::local_blks_written, pgssSharedState::lock, LW_SHARED, LWLockAcquire(), LWLockRelease(), Counters::max_time, Counters::mean_time, MemoryContextSwitchTo(), Counters::min_time, pgssEntry::mutex, pgssSharedState::mutex, pgssSharedState::n_writers, numeric_in(), ObjectIdGetDatum, pfree(), pg_any_to_server(), PG_STAT_STATEMENTS_COLS, PG_STAT_STATEMENTS_COLS_V1_0, PG_STAT_STATEMENTS_COLS_V1_1, PG_STAT_STATEMENTS_COLS_V1_2, PG_STAT_STATEMENTS_COLS_V1_3, PG_STAT_STATEMENTS_COLS_V1_8, PGSS_EXEC, PGSS_NUMKIND, PGSS_V1_0, PGSS_V1_1, PGSS_V1_2, PGSS_V1_3, PGSS_V1_8, qtext_fetch(), qtext_load_file(), pgssEntry::query_len, pgssEntry::query_offset, pgssHashKey::queryid, FunctionCallInfoBaseData::resultinfo, ReturnSetInfo::returnMode, Counters::rows, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, Counters::shared_blks_dirtied, Counters::shared_blks_hit, Counters::shared_blks_read, Counters::shared_blks_written, snprintf, SpinLockAcquire, SpinLockRelease, Counters::sum_var_time, Counters::temp_blks_read, Counters::temp_blks_written, Counters::total_time, tuplestore_begin_heap(), tuplestore_donestoring, tuplestore_putvalues(), TYPEFUNC_COMPOSITE, UINT64_FORMAT, pgssHashKey::userid, values, Counters::wal_bytes, Counters::wal_fpi, Counters::wal_records, and work_mem.

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

1574 {
1575  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1576  TupleDesc tupdesc;
1577  Tuplestorestate *tupstore;
1578  MemoryContext per_query_ctx;
1579  MemoryContext oldcontext;
1580  Oid userid = GetUserId();
1581  bool is_allowed_role = false;
1582  char *qbuffer = NULL;
1583  Size qbuffer_size = 0;
1584  Size extent = 0;
1585  int gc_count = 0;
1586  HASH_SEQ_STATUS hash_seq;
1587  pgssEntry *entry;
1588 
1589  /* Superusers or members of pg_read_all_stats members are allowed */
1590  is_allowed_role = is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_ALL_STATS);
1591 
1592  /* hash table must exist already */
1593  if (!pgss || !pgss_hash)
1594  ereport(ERROR,
1595  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1596  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
1597 
1598  /* check to see if caller supports us returning a tuplestore */
1599  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1600  ereport(ERROR,
1601  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1602  errmsg("set-valued function called in context that cannot accept a set")));
1603  if (!(rsinfo->allowedModes & SFRM_Materialize))
1604  ereport(ERROR,
1605  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1606  errmsg("materialize mode required, but it is not allowed in this context")));
1607 
1608  /* Switch into long-lived context to construct returned data structures */
1609  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
1610  oldcontext = MemoryContextSwitchTo(per_query_ctx);
1611 
1612  /* Build a tuple descriptor for our result type */
1613  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1614  elog(ERROR, "return type must be a row type");
1615 
1616  /*
1617  * Check we have the expected number of output arguments. Aside from
1618  * being a good safety check, we need a kluge here to detect API version
1619  * 1.1, which was wedged into the code in an ill-considered way.
1620  */
1621  switch (tupdesc->natts)
1622  {
1624  if (api_version != PGSS_V1_0)
1625  elog(ERROR, "incorrect number of output arguments");
1626  break;
1628  /* pg_stat_statements() should have told us 1.0 */
1629  if (api_version != PGSS_V1_0)
1630  elog(ERROR, "incorrect number of output arguments");
1631  api_version = PGSS_V1_1;
1632  break;
1634  if (api_version != PGSS_V1_2)
1635  elog(ERROR, "incorrect number of output arguments");
1636  break;
1638  if (api_version != PGSS_V1_3)
1639  elog(ERROR, "incorrect number of output arguments");
1640  break;
1642  if (api_version != PGSS_V1_8)
1643  elog(ERROR, "incorrect number of output arguments");
1644  break;
1645  default:
1646  elog(ERROR, "incorrect number of output arguments");
1647  }
1648 
1649  tupstore = tuplestore_begin_heap(true, false, work_mem);
1650  rsinfo->returnMode = SFRM_Materialize;
1651  rsinfo->setResult = tupstore;
1652  rsinfo->setDesc = tupdesc;
1653 
1654  MemoryContextSwitchTo(oldcontext);
1655 
1656  /*
1657  * We'd like to load the query text file (if needed) while not holding any
1658  * lock on pgss->lock. In the worst case we'll have to do this again
1659  * after we have the lock, but it's unlikely enough to make this a win
1660  * despite occasional duplicated work. We need to reload if anybody
1661  * writes to the file (either a retail qtext_store(), or a garbage
1662  * collection) between this point and where we've gotten shared lock. If
1663  * a qtext_store is actually in progress when we look, we might as well
1664  * skip the speculative load entirely.
1665  */
1666  if (showtext)
1667  {
1668  int n_writers;
1669 
1670  /* Take the mutex so we can examine variables */
1671  {
1672  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1673 
1674  SpinLockAcquire(&s->mutex);
1675  extent = s->extent;
1676  n_writers = s->n_writers;
1677  gc_count = s->gc_count;
1678  SpinLockRelease(&s->mutex);
1679  }
1680 
1681  /* No point in loading file now if there are active writers */
1682  if (n_writers == 0)
1683  qbuffer = qtext_load_file(&qbuffer_size);
1684  }
1685 
1686  /*
1687  * Get shared lock, load or reload the query text file if we must, and
1688  * iterate over the hashtable entries.
1689  *
1690  * With a large hash table, we might be holding the lock rather longer
1691  * than one could wish. However, this only blocks creation of new hash
1692  * table entries, and the larger the hash table the less likely that is to
1693  * be needed. So we can hope this is okay. Perhaps someday we'll decide
1694  * we need to partition the hash table to limit the time spent holding any
1695  * one lock.
1696  */
1698 
1699  if (showtext)
1700  {
1701  /*
1702  * Here it is safe to examine extent and gc_count without taking the
1703  * mutex. Note that although other processes might change
1704  * pgss->extent just after we look at it, the strings they then write
1705  * into the file cannot yet be referenced in the hashtable, so we
1706  * don't care whether we see them or not.
1707  *
1708  * If qtext_load_file fails, we just press on; we'll return NULL for
1709  * every query text.
1710  */
1711  if (qbuffer == NULL ||
1712  pgss->extent != extent ||
1713  pgss->gc_count != gc_count)
1714  {
1715  if (qbuffer)
1716  free(qbuffer);
1717  qbuffer = qtext_load_file(&qbuffer_size);
1718  }
1719  }
1720 
1721  hash_seq_init(&hash_seq, pgss_hash);
1722  while ((entry = hash_seq_search(&hash_seq)) != NULL)
1723  {
1725  bool nulls[PG_STAT_STATEMENTS_COLS];
1726  int i = 0;
1727  Counters tmp;
1728  double stddev;
1729  int64 queryid = entry->key.queryid;
1730 
1731  memset(values, 0, sizeof(values));
1732  memset(nulls, 0, sizeof(nulls));
1733 
1734  values[i++] = ObjectIdGetDatum(entry->key.userid);
1735  values[i++] = ObjectIdGetDatum(entry->key.dbid);
1736 
1737  if (is_allowed_role || entry->key.userid == userid)
1738  {
1739  if (api_version >= PGSS_V1_2)
1740  values[i++] = Int64GetDatumFast(queryid);
1741 
1742  if (showtext)
1743  {
1744  char *qstr = qtext_fetch(entry->query_offset,
1745  entry->query_len,
1746  qbuffer,
1747  qbuffer_size);
1748 
1749  if (qstr)
1750  {
1751  char *enc;
1752 
1753  enc = pg_any_to_server(qstr,
1754  entry->query_len,
1755  entry->encoding);
1756 
1757  values[i++] = CStringGetTextDatum(enc);
1758 
1759  if (enc != qstr)
1760  pfree(enc);
1761  }
1762  else
1763  {
1764  /* Just return a null if we fail to find the text */
1765  nulls[i++] = true;
1766  }
1767  }
1768  else
1769  {
1770  /* Query text not requested */
1771  nulls[i++] = true;
1772  }
1773  }
1774  else
1775  {
1776  /* Don't show queryid */
1777  if (api_version >= PGSS_V1_2)
1778  nulls[i++] = true;
1779 
1780  /*
1781  * Don't show query text, but hint as to the reason for not doing
1782  * so if it was requested
1783  */
1784  if (showtext)
1785  values[i++] = CStringGetTextDatum("<insufficient privilege>");
1786  else
1787  nulls[i++] = true;
1788  }
1789 
1790  /* copy counters to a local variable to keep locking time short */
1791  {
1792  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1793 
1794  SpinLockAcquire(&e->mutex);
1795  tmp = e->counters;
1796  SpinLockRelease(&e->mutex);
1797  }
1798 
1799  /* Skip entry if unexecuted (ie, it's a pending "sticky" entry) */
1800  if (IS_STICKY(tmp))
1801  continue;
1802 
1803  /* Note that we rely on PGSS_PLAN being 0 and PGSS_EXEC being 1. */
1804  for (int kind = 0; kind < PGSS_NUMKIND; kind++)
1805  {
1806  if (kind == PGSS_EXEC || api_version >= PGSS_V1_8)
1807  {
1808  values[i++] = Int64GetDatumFast(tmp.calls[kind]);
1809  values[i++] = Float8GetDatumFast(tmp.total_time[kind]);
1810  }
1811 
1812  if ((kind == PGSS_EXEC && api_version >= PGSS_V1_3) ||
1813  api_version >= PGSS_V1_8)
1814  {
1815  values[i++] = Float8GetDatumFast(tmp.min_time[kind]);
1816  values[i++] = Float8GetDatumFast(tmp.max_time[kind]);
1817  values[i++] = Float8GetDatumFast(tmp.mean_time[kind]);
1818 
1819  /*
1820  * Note we are calculating the population variance here, not
1821  * the sample variance, as we have data for the whole
1822  * population, so Bessel's correction is not used, and we
1823  * don't divide by tmp.calls - 1.
1824  */
1825  if (tmp.calls[kind] > 1)
1826  stddev = sqrt(tmp.sum_var_time[kind] / tmp.calls[kind]);
1827  else
1828  stddev = 0.0;
1829  values[i++] = Float8GetDatumFast(stddev);
1830  }
1831  }
1832  values[i++] = Int64GetDatumFast(tmp.rows);
1833  values[i++] = Int64GetDatumFast(tmp.shared_blks_hit);
1834  values[i++] = Int64GetDatumFast(tmp.shared_blks_read);
1835  if (api_version >= PGSS_V1_1)
1836  values[i++] = Int64GetDatumFast(tmp.shared_blks_dirtied);
1837  values[i++] = Int64GetDatumFast(tmp.shared_blks_written);
1838  values[i++] = Int64GetDatumFast(tmp.local_blks_hit);
1839  values[i++] = Int64GetDatumFast(tmp.local_blks_read);
1840  if (api_version >= PGSS_V1_1)
1841  values[i++] = Int64GetDatumFast(tmp.local_blks_dirtied);
1842  values[i++] = Int64GetDatumFast(tmp.local_blks_written);
1843  values[i++] = Int64GetDatumFast(tmp.temp_blks_read);
1844  values[i++] = Int64GetDatumFast(tmp.temp_blks_written);
1845  if (api_version >= PGSS_V1_1)
1846  {
1847  values[i++] = Float8GetDatumFast(tmp.blk_read_time);
1848  values[i++] = Float8GetDatumFast(tmp.blk_write_time);
1849  }
1850  if (api_version >= PGSS_V1_8)
1851  {
1852  char buf[256];
1853  Datum wal_bytes;
1854 
1855  values[i++] = Int64GetDatumFast(tmp.wal_records);
1856  values[i++] = Int64GetDatumFast(tmp.wal_fpi);
1857 
1858  snprintf(buf, sizeof buf, UINT64_FORMAT, tmp.wal_bytes);
1859 
1860  /* Convert to numeric. */
1861  wal_bytes = DirectFunctionCall3(numeric_in,
1862  CStringGetDatum(buf),
1863  ObjectIdGetDatum(0),
1864  Int32GetDatum(-1));
1865  values[i++] = wal_bytes;
1866  }
1867 
1868  Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
1869  api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
1870  api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
1871  api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
1872  api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
1873  -1 /* fail if you forget to update this assert */ ));
1874 
1875  tuplestore_putvalues(tupstore, tupdesc, values, nulls);
1876  }
1877 
1878  /* clean up and return the tuplestore */
1880 
1881  if (qbuffer)
1882  free(qbuffer);
1883 
1884  tuplestore_donestoring(tupstore);
1885 }
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull)
Definition: tuplestore.c:750
#define IsA(nodeptr, _type_)
Definition: nodes.h:584
#define PG_STAT_STATEMENTS_COLS_V1_3
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:207
double total_time[PGSS_NUMKIND]
#define PG_STAT_STATEMENTS_COLS_V1_8
#define PG_STAT_STATEMENTS_COLS_V1_0
int64 shared_blks_read
int64 local_blks_written
Oid GetUserId(void)
Definition: miscinit.c:478
int64 shared_blks_dirtied
double max_time[PGSS_NUMKIND]
#define tuplestore_donestoring(state)
Definition: tuplestore.h:60
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:694
Counters counters
int64 temp_blks_read
#define PG_STAT_STATEMENTS_COLS_V1_1
unsigned int Oid
Definition: postgres_ext.h:31
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1808
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
void pfree(void *pointer)
Definition: mcxt.c:1057
int64 shared_blks_hit
static HTAB * pgss_hash
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:45
struct pg_encoding enc
Definition: encode.c:516
fmNodePtr resultinfo
Definition: fmgr.h:89
static char * buf
Definition: pg_test_fsync.c:68
#define CStringGetDatum(X)
Definition: postgres.h:578
static char * qtext_load_file(Size *buffer_size)
#define IS_STICKY(c)
Datum numeric_in(PG_FUNCTION_ARGS)
Definition: numeric.c:621
int64 temp_blks_written
int64 shared_blks_written
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition: fmgr.h:630
#define SpinLockRelease(lock)
Definition: spin.h:64
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
int64 local_blks_read
uintptr_t Datum
Definition: postgres.h:367
double mean_time[PGSS_NUMKIND]
#define Int64GetDatumFast(X)
Definition: postgres.h:760
int work_mem
Definition: globals.c:122
pgssHashKey key
#define ereport(elevel,...)
Definition: elog.h:155
int allowedModes
Definition: execnodes.h:304
#define PG_STAT_STATEMENTS_COLS
#define free(a)
Definition: header.h:65
int64 calls[PGSS_NUMKIND]
bool is_member_of_role(Oid member, Oid role)
Definition: acl.c:4919
#define Float8GetDatumFast(X)
Definition: postgres.h:761
SetFunctionReturnMode returnMode
Definition: execnodes.h:306
double min_time[PGSS_NUMKIND]
double blk_read_time
#define Assert(condition)
Definition: c.h:804
int64 local_blks_dirtied
int64 local_blks_hit
size_t Size
Definition: c.h:540
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1206
double blk_write_time
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:232
double sum_var_time[PGSS_NUMKIND]
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
Tuplestorestate * setResult
Definition: execnodes.h:309
static Datum values[MAXATTR]
Definition: bootstrap.c:165
ExprContext * econtext
Definition: execnodes.h:302
#define Int32GetDatum(X)
Definition: postgres.h:479
e
Definition: preproc-init.c:82
TupleDesc setDesc
Definition: execnodes.h:310
int errmsg(const char *fmt,...)
Definition: elog.c:905
#define elog(elevel,...)
Definition: elog.h:227
int i
#define CStringGetTextDatum(s)
Definition: builtins.h:82
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:619
#define snprintf
Definition: port.h:216
#define UINT64_FORMAT
Definition: c.h:484
#define PG_STAT_STATEMENTS_COLS_V1_2
static char * qtext_fetch(Size query_offset, int query_len, char *buffer, Size buffer_size)

◆ pg_stat_statements_reset()

Datum pg_stat_statements_reset ( PG_FUNCTION_ARGS  )

Definition at line 1501 of file pg_stat_statements.c.

References entry_reset(), and PG_RETURN_VOID.

1502 {
1503  entry_reset(0, 0, 0);
1504 
1505  PG_RETURN_VOID();
1506 }
static void entry_reset(Oid userid, Oid dbid, uint64 queryid)
#define PG_RETURN_VOID()
Definition: fmgr.h:349

◆ pg_stat_statements_reset_1_7()

Datum pg_stat_statements_reset_1_7 ( PG_FUNCTION_ARGS  )

Definition at line 1482 of file pg_stat_statements.c.

References pgssHashKey::dbid, entry_reset(), PG_GETARG_INT64, PG_GETARG_OID, PG_RETURN_VOID, pgssHashKey::queryid, and pgssHashKey::userid.

1483 {
1484  Oid userid;
1485  Oid dbid;
1486  uint64 queryid;
1487 
1488  userid = PG_GETARG_OID(0);
1489  dbid = PG_GETARG_OID(1);
1490  queryid = (uint64) PG_GETARG_INT64(2);
1491 
1492  entry_reset(userid, dbid, queryid);
1493 
1494  PG_RETURN_VOID();
1495 }
unsigned int Oid
Definition: postgres_ext.h:31
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
static void entry_reset(Oid userid, Oid dbid, uint64 queryid)
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283

◆ pgss_ExecutorEnd()

static void pgss_ExecutorEnd ( QueryDesc queryDesc)
static

Definition at line 1100 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1101 {
1102  uint64 queryId = queryDesc->plannedstmt->queryId;
1103 
1104  if (queryId != UINT64CONST(0) && queryDesc->totaltime &&
1106  {
1107  /*
1108  * Make sure stats accumulation is done. (Note: it's okay if several
1109  * levels of hook all do this.)
1110  */
1111  InstrEndLoop(queryDesc->totaltime);
1112 
1113  pgss_store(queryDesc->sourceText,
1114  queryId,
1115  queryDesc->plannedstmt->stmt_location,
1116  queryDesc->plannedstmt->stmt_len,
1117  PGSS_EXEC,
1118  queryDesc->totaltime->total * 1000.0, /* convert to msec */
1119  queryDesc->estate->es_processed,
1120  &queryDesc->totaltime->bufusage,
1121  &queryDesc->totaltime->walusage,
1122  NULL);
1123  }
1124 
1125  if (prev_ExecutorEnd)
1126  prev_ExecutorEnd(queryDesc);
1127  else
1128  standard_ExecutorEnd(queryDesc);
1129 }
WalUsage walusage
Definition: instrument.h:75
EState * estate
Definition: execdesc.h:48
static ExecutorEnd_hook_type prev_ExecutorEnd
#define pgss_enabled(level)
void standard_ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:457
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, pgssJumbleState *jstate)
static int exec_nested_level
int stmt_len
Definition: plannodes.h:90
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:121
int stmt_location
Definition: plannodes.h:89
BufferUsage bufusage
Definition: instrument.h:74
struct Instrumentation * totaltime
Definition: execdesc.h:55
uint64 es_processed
Definition: execnodes.h:576
const char * sourceText
Definition: execdesc.h:38
uint64 queryId
Definition: plannodes.h:48
PlannedStmt * plannedstmt
Definition: execdesc.h:37

◆ pgss_ExecutorFinish()

static void pgss_ExecutorFinish ( QueryDesc queryDesc)
static

Definition at line 1079 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1080 {
1082  PG_TRY();
1083  {
1084  if (prev_ExecutorFinish)
1085  prev_ExecutorFinish(queryDesc);
1086  else
1087  standard_ExecutorFinish(queryDesc);
1088  }
1089  PG_FINALLY();
1090  {
1092  }
1093  PG_END_TRY();
1094 }
static int exec_nested_level
void standard_ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:397
static ExecutorFinish_hook_type prev_ExecutorFinish
#define PG_FINALLY()
Definition: elog.h:325
#define PG_TRY()
Definition: elog.h:308
#define PG_END_TRY()
Definition: elog.h:333

◆ pgss_ExecutorRun()

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

Definition at line 1057 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1059 {
1061  PG_TRY();
1062  {
1063  if (prev_ExecutorRun)
1064  prev_ExecutorRun(queryDesc, direction, count, execute_once);
1065  else
1066  standard_ExecutorRun(queryDesc, direction, count, execute_once);
1067  }
1068  PG_FINALLY();
1069  {
1071  }
1072  PG_END_TRY();
1073 }
void standard_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:298
static ExecutorRun_hook_type prev_ExecutorRun
static int exec_nested_level
#define PG_FINALLY()
Definition: elog.h:325
#define PG_TRY()
Definition: elog.h:308
#define PG_END_TRY()
Definition: elog.h:333

◆ pgss_ExecutorStart()

static void pgss_ExecutorStart ( QueryDesc queryDesc,
int  eflags 
)
static

Definition at line 1023 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1024 {
1025  if (prev_ExecutorStart)
1026  prev_ExecutorStart(queryDesc, eflags);
1027  else
1028  standard_ExecutorStart(queryDesc, eflags);
1029 
1030  /*
1031  * If query has queryId zero, don't track it. This prevents double
1032  * counting of optimizable statements that are directly contained in
1033  * utility statements.
1034  */
1035  if (pgss_enabled(exec_nested_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0))
1036  {
1037  /*
1038  * Set up to track total elapsed time in ExecutorRun. Make sure the
1039  * space is allocated in the per-query context so it will go away at
1040  * ExecutorEnd.
1041  */
1042  if (queryDesc->totaltime == NULL)
1043  {
1044  MemoryContext oldcxt;
1045 
1046  oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
1047  queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL);
1048  MemoryContextSwitchTo(oldcxt);
1049  }
1050  }
1051 }
EState * estate
Definition: execdesc.h:48
void standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:138
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define pgss_enabled(level)
Instrumentation * InstrAlloc(int n, int instrument_options)
Definition: instrument.c:31
static ExecutorStart_hook_type prev_ExecutorStart
static int exec_nested_level
MemoryContext es_query_cxt
Definition: execnodes.h:572
struct Instrumentation * totaltime
Definition: execdesc.h:55
uint64 queryId
Definition: plannodes.h:48
PlannedStmt * plannedstmt
Definition: execdesc.h:37

◆ pgss_hash_string()

static uint64 pgss_hash_string ( const char *  str,
int  len 
)
static

Definition at line 1243 of file pg_stat_statements.c.

References DatumGetUInt64, and hash_any_extended().

Referenced by pgss_store().

1244 {
1245  return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
1246  len, 0));
1247 }
static Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition: hashfn.h:37
#define DatumGetUInt64(X)
Definition: postgres.h:634

◆ pgss_memsize()

static Size pgss_memsize ( void  )
static

Definition at line 1932 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1933 {
1934  Size size;
1935 
1936  size = MAXALIGN(sizeof(pgssSharedState));
1937  size = add_size(size, hash_estimate_size(pgss_max, sizeof(pgssEntry)));
1938 
1939  return size;
1940 }
static int pgss_max
Size hash_estimate_size(long num_entries, Size entrysize)
Definition: dynahash.c:780
Size add_size(Size s1, Size s2)
Definition: shmem.c:502
size_t Size
Definition: c.h:540
#define MAXALIGN(LEN)
Definition: c.h:757

◆ pgss_planner()

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

Definition at line 928 of file pg_stat_statements.c.

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

Referenced by _PG_init().

932 {
933  PlannedStmt *result;
934 
935  /*
936  * We can't process the query if no query_string is provided, as
937  * pgss_store needs it. We also ignore query without queryid, as it would
938  * be treated as a utility statement, which may not be the case.
939  *
940  * Note that planner_hook can be called from the planner itself, so we
941  * have a specific nesting level for the planner. However, utility
942  * commands containing optimizable statements can also call the planner,
943  * same for regular DML (for instance for underlying foreign key queries).
944  * So testing the planner nesting level only is not enough to detect real
945  * top level planner call.
946  */
948  && pgss_track_planning && query_string
949  && parse->queryId != UINT64CONST(0))
950  {
951  instr_time start;
953  BufferUsage bufusage_start,
954  bufusage;
955  WalUsage walusage_start,
956  walusage;
957 
958  /* We need to track buffer usage as the planner can access them. */
959  bufusage_start = pgBufferUsage;
960 
961  /*
962  * Similarly the planner could write some WAL records in some cases
963  * (e.g. setting a hint bit with those being WAL-logged)
964  */
965  walusage_start = pgWalUsage;
966  INSTR_TIME_SET_CURRENT(start);
967 
969  PG_TRY();
970  {
971  if (prev_planner_hook)
972  result = prev_planner_hook(parse, query_string, cursorOptions,
973  boundParams);
974  else
975  result = standard_planner(parse, query_string, cursorOptions,
976  boundParams);
977  }
978  PG_FINALLY();
979  {
981  }
982  PG_END_TRY();
983 
984  INSTR_TIME_SET_CURRENT(duration);
985  INSTR_TIME_SUBTRACT(duration, start);
986 
987  /* calc differences of buffer counters. */
988  memset(&bufusage, 0, sizeof(BufferUsage));
989  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
990 
991  /* calc differences of WAL counters. */
992  memset(&walusage, 0, sizeof(WalUsage));
993  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
994 
995  pgss_store(query_string,
996  parse->queryId,
997  parse->stmt_location,
998  parse->stmt_len,
999  PGSS_PLAN,
1000  INSTR_TIME_GET_MILLISEC(duration),
1001  0,
1002  &bufusage,
1003  &walusage,
1004  NULL);
1005  }
1006  else
1007  {
1008  if (prev_planner_hook)
1009  result = prev_planner_hook(parse, query_string, cursorOptions,
1010  boundParams);
1011  else
1012  result = standard_planner(parse, query_string, cursorOptions,
1013  boundParams);
1014  }
1015 
1016  return result;
1017 }
int stmt_location
Definition: parsenodes.h:181
WalUsage pgWalUsage
Definition: instrument.c:22
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:202
struct timeval instr_time
Definition: instr_time.h:150
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition: instrument.c:255
#define pgss_enabled(level)
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, pgssJumbleState *jstate)
static int exec_nested_level
int duration
Definition: pgbench.c:147
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition: instrument.c:225
static planner_hook_type prev_planner_hook
static bool pgss_track_planning
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
uint64 queryId
Definition: parsenodes.h:116
#define PG_FINALLY()
Definition: elog.h:325
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:156
static int plan_nested_level
PlannedStmt * standard_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: planner.c:278
#define PG_TRY()
Definition: elog.h:308
int stmt_len
Definition: parsenodes.h:182
#define PG_END_TRY()
Definition: elog.h:333
BufferUsage pgBufferUsage
Definition: instrument.c:20

◆ pgss_post_parse_analyze()

static void pgss_post_parse_analyze ( ParseState pstate,
Query query 
)
static

Definition at line 854 of file pg_stat_statements.c.

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

Referenced by _PG_init().

855 {
856  pgssJumbleState jstate;
857 
859  prev_post_parse_analyze_hook(pstate, query);
860 
861  /* Assert we didn't do this already */
862  Assert(query->queryId == UINT64CONST(0));
863 
864  /* Safety check... */
866  return;
867 
868  /*
869  * Utility statements get queryId zero. We do this even in cases where
870  * the statement contains an optimizable statement for which a queryId
871  * could be derived (such as EXPLAIN or DECLARE CURSOR). For such cases,
872  * runtime control will first go through ProcessUtility and then the
873  * executor, and we don't want the executor hooks to do anything, since we
874  * are already measuring the statement's costs at the utility level.
875  */
876  if (query->utilityStmt)
877  {
878  query->queryId = UINT64CONST(0);
879  return;
880  }
881 
882  /* Set up workspace for query jumbling */
883  jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
884  jstate.jumble_len = 0;
885  jstate.clocations_buf_size = 32;
886  jstate.clocations = (pgssLocationLen *)
887  palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
888  jstate.clocations_count = 0;
889  jstate.highest_extern_param_id = 0;
890 
891  /* Compute query ID and mark the Query node with it */
892  JumbleQuery(&jstate, query);
893  query->queryId =
895 
896  /*
897  * If we are unlucky enough to get a hash of zero, use 1 instead, to
898  * prevent confusion with the utility-statement case.
899  */
900  if (query->queryId == UINT64CONST(0))
901  query->queryId = UINT64CONST(1);
902 
903  /*
904  * If we were able to identify any ignorable constants, we immediately
905  * create a hash table entry for the query, so that we can record the
906  * normalized form of the query string. If there were no such constants,
907  * the normalized string would be the same as the query text anyway, so
908  * there's no need for an early entry.
909  */
910  if (jstate.clocations_count > 0)
911  pgss_store(pstate->p_sourcetext,
912  query->queryId,
913  query->stmt_location,
914  query->stmt_len,
915  PGSS_INVALID,
916  0,
917  0,
918  NULL,
919  NULL,
920  &jstate);
921 }
int stmt_location
Definition: parsenodes.h:181
#define pgss_enabled(level)
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, pgssJumbleState *jstate)
unsigned char * jumble
static int exec_nested_level
#define JUMBLE_SIZE
Node * utilityStmt
Definition: parsenodes.h:120
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
static Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition: hashfn.h:37
static pgssSharedState * pgss
pgssLocationLen * clocations
static HTAB * pgss_hash
static void JumbleQuery(pgssJumbleState *jstate, Query *query)
const char * p_sourcetext
Definition: parse_node.h:180
uint64 queryId
Definition: parsenodes.h:116
#define DatumGetUInt64(X)
Definition: postgres.h:634
#define Assert(condition)
Definition: c.h:804
void * palloc(Size size)
Definition: mcxt.c:950
int stmt_len
Definition: parsenodes.h:182

◆ pgss_ProcessUtility()

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

Definition at line 1135 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1139 {
1140  Node *parsetree = pstmt->utilityStmt;
1141 
1142  /*
1143  * If it's an EXECUTE statement, we don't track it and don't increment the
1144  * nesting level. This allows the cycles to be charged to the underlying
1145  * PREPARE instead (by the Executor hooks), which is much more useful.
1146  *
1147  * We also don't track execution of PREPARE. If we did, we would get one
1148  * hash table entry for the PREPARE (with hash calculated from the query
1149  * string), and then a different one with the same query string (but hash
1150  * calculated from the query tree) would be used to accumulate costs of
1151  * ensuing EXECUTEs. This would be confusing, and inconsistent with other
1152  * cases where planning time is not included at all.
1153  *
1154  * Likewise, we don't track execution of DEALLOCATE.
1155  */
1157  !IsA(parsetree, ExecuteStmt) &&
1158  !IsA(parsetree, PrepareStmt) &&
1159  !IsA(parsetree, DeallocateStmt))
1160  {
1161  instr_time start;
1163  uint64 rows;
1164  BufferUsage bufusage_start,
1165  bufusage;
1166  WalUsage walusage_start,
1167  walusage;
1168 
1169  bufusage_start = pgBufferUsage;
1170  walusage_start = pgWalUsage;
1171  INSTR_TIME_SET_CURRENT(start);
1172 
1174  PG_TRY();
1175  {
1176  if (prev_ProcessUtility)
1177  prev_ProcessUtility(pstmt, queryString,
1178  context, params, queryEnv,
1179  dest, qc);
1180  else
1181  standard_ProcessUtility(pstmt, queryString,
1182  context, params, queryEnv,
1183  dest, qc);
1184  }
1185  PG_FINALLY();
1186  {
1188  }
1189  PG_END_TRY();
1190 
1191  INSTR_TIME_SET_CURRENT(duration);
1192  INSTR_TIME_SUBTRACT(duration, start);
1193 
1194  /*
1195  * Track the total number of rows retrieved or affected by the utility
1196  * statements of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED
1197  * VIEW, REFRESH MATERIALIZED VIEW and SELECT INTO.
1198  */
1199  rows = (qc && (qc->commandTag == CMDTAG_COPY ||
1200  qc->commandTag == CMDTAG_FETCH ||
1201  qc->commandTag == CMDTAG_SELECT ||
1202  qc->commandTag == CMDTAG_REFRESH_MATERIALIZED_VIEW)) ?
1203  qc->nprocessed : 0;
1204 
1205  /* calc differences of buffer counters. */
1206  memset(&bufusage, 0, sizeof(BufferUsage));
1207  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
1208 
1209  /* calc differences of WAL counters. */
1210  memset(&walusage, 0, sizeof(WalUsage));
1211  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
1212 
1213  pgss_store(queryString,
1214  0, /* signal that it's a utility stmt */
1215  pstmt->stmt_location,
1216  pstmt->stmt_len,
1217  PGSS_EXEC,
1218  INSTR_TIME_GET_MILLISEC(duration),
1219  rows,
1220  &bufusage,
1221  &walusage,
1222  NULL);
1223  }
1224  else
1225  {
1226  if (prev_ProcessUtility)
1227  prev_ProcessUtility(pstmt, queryString,
1228  context, params, queryEnv,
1229  dest, qc);
1230  else
1231  standard_ProcessUtility(pstmt, queryString,
1232  context, params, queryEnv,
1233  dest, qc);
1234  }
1235 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:584
static ProcessUtility_hook_type prev_ProcessUtility
WalUsage pgWalUsage
Definition: instrument.c:22
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:202
struct timeval instr_time
Definition: instr_time.h:150
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition: instrument.c:255
#define pgss_enabled(level)
Definition: nodes.h:533
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, pgssJumbleState *jstate)
static int exec_nested_level
int stmt_len
Definition: plannodes.h:90
int duration
Definition: pgbench.c:147
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition: instrument.c:225
uint64 nprocessed
Definition: cmdtag.h:31
void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:542
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
int stmt_location
Definition: plannodes.h:89
Node * utilityStmt
Definition: plannodes.h:86
#define PG_FINALLY()
Definition: elog.h:325
CommandTag commandTag
Definition: cmdtag.h:30
static bool pgss_track_utility
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:156
#define PG_TRY()
Definition: elog.h:308
#define PG_END_TRY()
Definition: elog.h:333
BufferUsage pgBufferUsage
Definition: instrument.c:20

◆ pgss_shmem_shutdown()

static void pgss_shmem_shutdown ( int  code,
Datum  arg 
)
static

Definition at line 754 of file pg_stat_statements.c.

References AllocateFile(), durable_rename(), ereport, errcode_for_file_access(), errmsg(), error(), free, FreeFile(), hash_get_num_entries(), hash_seq_init(), hash_seq_search(), hash_seq_term(), LOG, PG_BINARY_W, PGSS_DUMP_FILE, PGSS_FILE_HEADER, PGSS_PG_MAJOR_VERSION, pgss_save, PGSS_TEXT_FILE, qtext_fetch(), qtext_load_file(), pgssEntry::query_len, pgssEntry::query_offset, and pgssSharedState::stats.

Referenced by pgss_shmem_startup().

755 {
756  FILE *file;
757  char *qbuffer = NULL;
758  Size qbuffer_size = 0;
759  HASH_SEQ_STATUS hash_seq;
760  int32 num_entries;
761  pgssEntry *entry;
762 
763  /* Don't try to dump during a crash. */
764  if (code)
765  return;
766 
767  /* Safety check ... shouldn't get here unless shmem is set up. */
768  if (!pgss || !pgss_hash)
769  return;
770 
771  /* Don't dump if told not to. */
772  if (!pgss_save)
773  return;
774 
775  file = AllocateFile(PGSS_DUMP_FILE ".tmp", PG_BINARY_W);
776  if (file == NULL)
777  goto error;
778 
779  if (fwrite(&PGSS_FILE_HEADER, sizeof(uint32), 1, file) != 1)
780  goto error;
781  if (fwrite(&PGSS_PG_MAJOR_VERSION, sizeof(uint32), 1, file) != 1)
782  goto error;
783  num_entries = hash_get_num_entries(pgss_hash);
784  if (fwrite(&num_entries, sizeof(int32), 1, file) != 1)
785  goto error;
786 
787  qbuffer = qtext_load_file(&qbuffer_size);
788  if (qbuffer == NULL)
789  goto error;
790 
791  /*
792  * When serializing to disk, we store query texts immediately after their
793  * entry data. Any orphaned query texts are thereby excluded.
794  */
795  hash_seq_init(&hash_seq, pgss_hash);
796  while ((entry = hash_seq_search(&hash_seq)) != NULL)
797  {
798  int len = entry->query_len;
799  char *qstr = qtext_fetch(entry->query_offset, len,
800  qbuffer, qbuffer_size);
801 
802  if (qstr == NULL)
803  continue; /* Ignore any entries with bogus texts */
804 
805  if (fwrite(entry, sizeof(pgssEntry), 1, file) != 1 ||
806  fwrite(qstr, 1, len + 1, file) != len + 1)
807  {
808  /* note: we assume hash_seq_term won't change errno */
809  hash_seq_term(&hash_seq);
810  goto error;
811  }
812  }
813 
814  /* Dump global statistics for pg_stat_statements */
815  if (fwrite(&pgss->stats, sizeof(pgssGlobalStats), 1, file) != 1)
816  goto error;
817 
818  free(qbuffer);
819  qbuffer = NULL;
820 
821  if (FreeFile(file))
822  {
823  file = NULL;
824  goto error;
825  }
826 
827  /*
828  * Rename file into place, so we atomically replace any old one.
829  */
831 
832  /* Unlink query-texts file; it's not needed while shutdown */
833  unlink(PGSS_TEXT_FILE);
834 
835  return;
836 
837 error:
838  ereport(LOG,
840  errmsg("could not write file \"%s\": %m",
841  PGSS_DUMP_FILE ".tmp")));
842  if (qbuffer)
843  free(qbuffer);
844  if (file)
845  FreeFile(file);
846  unlink(PGSS_DUMP_FILE ".tmp");
847  unlink(PGSS_TEXT_FILE);
848 }
static void error(void)
Definition: sql-dyntest.c:147
static const uint32 PGSS_PG_MAJOR_VERSION
#define PGSS_DUMP_FILE
#define PG_BINARY_W
Definition: c.h:1274
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1382
#define LOG
Definition: elog.h:26
signed int int32
Definition: c.h:429
static pgssSharedState * pgss
static HTAB * pgss_hash
int errcode_for_file_access(void)
Definition: elog.c:717
static char * qtext_load_file(Size *buffer_size)
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2354
unsigned int uint32
Definition: c.h:441
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:687
pgssGlobalStats stats
#define ereport(elevel,...)
Definition: elog.h:155
#define free(a)
Definition: header.h:65
size_t Size
Definition: c.h:540
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
int FreeFile(FILE *file)
Definition: fd.c:2553
int errmsg(const char *fmt,...)
Definition: elog.c:905
static const uint32 PGSS_FILE_HEADER
#define PGSS_TEXT_FILE
void hash_seq_term(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1512
static bool pgss_save
static char * qtext_fetch(Size query_offset, int query_len, char *buffer, Size buffer_size)

◆ pgss_shmem_startup()

static void pgss_shmem_startup ( void  )
static

Definition at line 530 of file pg_stat_statements.c.

References AllocateFile(), ASSUMED_LENGTH_INIT, ASSUMED_MEDIAN_INIT, pgssEntry::counters, pgssSharedState::cur_median_usage, pgssGlobalStats::dealloc, pgssEntry::encoding, entry_alloc(), HASHCTL::entrysize, ereport, errcode(), errcode_for_file_access(), errmsg(), pgssSharedState::extent, FreeFile(), pgssSharedState::gc_count, GetCurrentTimestamp(), GetNamedLWLockTranche(), HASH_BLOBS, HASH_ELEM, header(), i, IS_STICKY, IsUnderPostmaster, pgssEntry::key, HASHCTL::keysize, pgssSharedState::lock, LOG, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), Max, pgssSharedState::mean_query_len, pgssSharedState::mutex, pgssSharedState::n_writers, on_shmem_exit(), palloc(), pfree(), PG_BINARY_R, PG_BINARY_W, PG_VALID_BE_ENCODING, PGSS_DUMP_FILE, PGSS_FILE_HEADER, pgss_max, PGSS_PG_MAJOR_VERSION, pgss_save, pgss_shmem_shutdown(), PGSS_TEXT_FILE, prev_shmem_startup_hook, pgssEntry::query_len, repalloc(), ShmemInitHash(), ShmemInitStruct(), SpinLockInit, pgssSharedState::stats, and pgssGlobalStats::stats_reset.

Referenced by _PG_init().

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

◆ pgss_store()

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

Definition at line 1264 of file pg_stat_statements.c.

References Assert, BufferUsage::blk_read_time, Counters::blk_read_time, BufferUsage::blk_write_time, Counters::blk_write_time, Counters::calls, pgssEntry::counters, pgssHashKey::dbid, encoding, entry_alloc(), pgssSharedState::gc_count, gc_qtexts(), generate_normalized_query(), GetDatabaseEncoding(), GetUserId(), HASH_FIND, hash_search(), INSTR_TIME_GET_MILLISEC, IS_STICKY, sort-test::key, BufferUsage::local_blks_dirtied, Counters::local_blks_dirtied, BufferUsage::local_blks_hit, Counters::local_blks_hit, BufferUsage::local_blks_read, Counters::local_blks_read, BufferUsage::local_blks_written, Counters::local_blks_written, pgssSharedState::lock, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), Counters::max_time, Counters::mean_time, Counters::min_time, pgssEntry::mutex, MyDatabaseId, need_gc_qtexts(), pfree(), PGSS_EXEC, pgss_hash_string(), PGSS_PLAN, qtext_store(), pgssHashKey::queryid, Counters::rows, scanner_isspace(), BufferUsage::shared_blks_dirtied, Counters::shared_blks_dirtied, BufferUsage::shared_blks_hit, Counters::shared_blks_hit, BufferUsage::shared_blks_read, Counters::shared_blks_read, BufferUsage::shared_blks_written, Counters::shared_blks_written, SpinLockAcquire, SpinLockRelease, Counters::sum_var_time, BufferUsage::temp_blks_read, Counters::temp_blks_read, BufferUsage::temp_blks_written, Counters::temp_blks_written, Counters::total_time, Counters::usage, USAGE_EXEC, USAGE_INIT, pgssHashKey::userid, WalUsage::wal_bytes, Counters::wal_bytes, WalUsage::wal_fpi, Counters::wal_fpi, WalUsage::wal_records, and Counters::wal_records.

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

1271 {
1272  pgssHashKey key;
1273  pgssEntry *entry;
1274  char *norm_query = NULL;
1275  int encoding = GetDatabaseEncoding();
1276 
1277  Assert(query != NULL);
1278 
1279  /* Safety check... */
1280  if (!pgss || !pgss_hash)
1281  return;
1282 
1283  /*
1284  * Confine our attention to the relevant part of the string, if the query
1285  * is a portion of a multi-statement source string.
1286  *
1287  * First apply starting offset, unless it's -1 (unknown).
1288  */
1289  if (query_location >= 0)
1290  {
1291  Assert(query_location <= strlen(query));
1292  query += query_location;
1293  /* Length of 0 (or -1) means "rest of string" */
1294  if (query_len <= 0)
1295  query_len = strlen(query);
1296  else
1297  Assert(query_len <= strlen(query));
1298  }
1299  else
1300  {
1301  /* If query location is unknown, distrust query_len as well */
1302  query_location = 0;
1303  query_len = strlen(query);
1304  }
1305 
1306  /*
1307  * Discard leading and trailing whitespace, too. Use scanner_isspace()
1308  * not libc's isspace(), because we want to match the lexer's behavior.
1309  */
1310  while (query_len > 0 && scanner_isspace(query[0]))
1311  query++, query_location++, query_len--;
1312  while (query_len > 0 && scanner_isspace(query[query_len - 1]))
1313  query_len--;
1314 
1315  /*
1316  * For utility statements, we just hash the query string to get an ID.
1317  */
1318  if (queryId == UINT64CONST(0))
1319  {
1320  queryId = pgss_hash_string(query, query_len);
1321 
1322  /*
1323  * If we are unlucky enough to get a hash of zero(invalid), use
1324  * queryID as 2 instead, queryID 1 is already in use for normal
1325  * statements.
1326  */
1327  if (queryId == UINT64CONST(0))
1328  queryId = UINT64CONST(2);
1329  }
1330 
1331  /* Set up key for hashtable search */
1332  key.userid = GetUserId();
1333  key.dbid = MyDatabaseId;
1334  key.queryid = queryId;
1335 
1336  /* Lookup the hash table entry with shared lock. */
1338 
1339  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
1340 
1341  /* Create new entry, if not present */
1342  if (!entry)
1343  {
1344  Size query_offset;
1345  int gc_count;
1346  bool stored;
1347  bool do_gc;
1348 
1349  /*
1350  * Create a new, normalized query string if caller asked. We don't
1351  * need to hold the lock while doing this work. (Note: in any case,
1352  * it's possible that someone else creates a duplicate hashtable entry
1353  * in the interval where we don't hold the lock below. That case is
1354  * handled by entry_alloc.)
1355  */
1356  if (jstate)
1357  {
1359  norm_query = generate_normalized_query(jstate, query,
1360  query_location,
1361  &query_len);
1363  }
1364 
1365  /* Append new query text to file with only shared lock held */
1366  stored = qtext_store(norm_query ? norm_query : query, query_len,
1367  &query_offset, &gc_count);
1368 
1369  /*
1370  * Determine whether we need to garbage collect external query texts
1371  * while the shared lock is still held. This micro-optimization
1372  * avoids taking the time to decide this while holding exclusive lock.
1373  */
1374  do_gc = need_gc_qtexts();
1375 
1376  /* Need exclusive lock to make a new hashtable entry - promote */
1379 
1380  /*
1381  * A garbage collection may have occurred while we weren't holding the
1382  * lock. In the unlikely event that this happens, the query text we
1383  * stored above will have been garbage collected, so write it again.
1384  * This should be infrequent enough that doing it while holding
1385  * exclusive lock isn't a performance problem.
1386  */
1387  if (!stored || pgss->gc_count != gc_count)
1388  stored = qtext_store(norm_query ? norm_query : query, query_len,
1389  &query_offset, NULL);
1390 
1391  /* If we failed to write to the text file, give up */
1392  if (!stored)
1393  goto done;
1394 
1395  /* OK to create a new hashtable entry */
1396  entry = entry_alloc(&key, query_offset, query_len, encoding,
1397  jstate != NULL);
1398 
1399  /* If needed, perform garbage collection while exclusive lock held */
1400  if (do_gc)
1401  gc_qtexts();
1402  }
1403 
1404  /* Increment the counts, except when jstate is not NULL */
1405  if (!jstate)
1406  {
1407  /*
1408  * Grab the spinlock while updating the counters (see comment about
1409  * locking rules at the head of the file)
1410  */
1411  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1412 
1413  Assert(kind == PGSS_PLAN || kind == PGSS_EXEC);
1414 
1415  SpinLockAcquire(&e->mutex);
1416 
1417  /* "Unstick" entry if it was previously sticky */
1418  if (IS_STICKY(e->counters))
1419  e->counters.usage = USAGE_INIT;
1420 
1421  e->counters.calls[kind] += 1;
1422  e->counters.total_time[kind] += total_time;
1423 
1424  if (e->counters.calls[kind] == 1)
1425  {
1426  e->counters.min_time[kind] = total_time;
1427  e->counters.max_time[kind] = total_time;
1428  e->counters.mean_time[kind] = total_time;
1429  }
1430  else
1431  {
1432  /*
1433  * Welford's method for accurately computing variance. See
1434  * <http://www.johndcook.com/blog/standard_deviation/>
1435  */
1436  double old_mean = e->counters.mean_time[kind];
1437 
1438  e->counters.mean_time[kind] +=
1439  (total_time - old_mean) / e->counters.calls[kind];
1440  e->counters.sum_var_time[kind] +=
1441  (total_time - old_mean) * (total_time - e->counters.mean_time[kind]);
1442 
1443  /* calculate min and max time */
1444  if (e->counters.min_time[kind] > total_time)
1445  e->counters.min_time[kind] = total_time;
1446  if (e->counters.max_time[kind] < total_time)
1447  e->counters.max_time[kind] = total_time;
1448  }
1449  e->counters.rows += rows;
1450  e->counters.shared_blks_hit += bufusage->shared_blks_hit;
1451  e->counters.shared_blks_read += bufusage->shared_blks_read;
1454  e->counters.local_blks_hit += bufusage->local_blks_hit;
1455  e->counters.local_blks_read += bufusage->local_blks_read;
1458  e->counters.temp_blks_read += bufusage->temp_blks_read;
1462  e->counters.usage += USAGE_EXEC(total_time);
1463  e->counters.wal_records += walusage->wal_records;
1464  e->counters.wal_fpi += walusage->wal_fpi;
1465  e->counters.wal_bytes += walusage->wal_bytes;
1466 
1467  SpinLockRelease(&e->mutex);
1468  }
1469 
1470 done:
1472 
1473  /* We postpone this clean-up until we're out of the lock */
1474  if (norm_query)
1475  pfree(norm_query);
1476 }
long local_blks_hit
Definition: instrument.h:25
static uint64 pgss_hash_string(const char *str, int len)
long local_blks_dirtied
Definition: instrument.h:27
double total_time[PGSS_NUMKIND]
long local_blks_read
Definition: instrument.h:26
int64 shared_blks_read
<