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 dependency graph for pg_stat_statements.c:

Go to the source code of this file.

Data Structures

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

Macros

#define PGSS_DUMP_FILE   PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"
 
#define PGSS_TEXT_FILE   PG_STAT_TMP_DIR "/pgss_query_texts.stat"
 
#define USAGE_EXEC(duration)   (1.0)
 
#define USAGE_INIT   (1.0) /* including initial planning */
 
#define ASSUMED_MEDIAN_INIT   (10.0) /* initial assumed median usage */
 
#define ASSUMED_LENGTH_INIT   1024 /* initial assumed mean query length */
 
#define USAGE_DECREASE_FACTOR   (0.99) /* decreased every entry_dealloc */
 
#define STICKY_DECREASE_FACTOR   (0.50) /* factor for sticky entries */
 
#define USAGE_DEALLOC_PERCENT   5 /* free this % of entries at once */
 
#define 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 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 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)
 
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, int encoding)
 
static void fill_in_constant_lengths (pgssJumbleState *jstate, const char *query, int query_loc)
 
static int comp_location (const void *a, const void *b)
 
Datum pg_stat_statements_reset_1_7 (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements_reset (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements_1_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)
 
static int entry_cmp (const void *lhs, const void *rhs)
 

Variables

 PG_MODULE_MAGIC
 
static const uint32 PGSS_FILE_HEADER = 0x20171004
 
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100
 
static int 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 2578 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 2580 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 110 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 109 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 116 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 1485 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 1480 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 1481 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 1482 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 1483 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 1484 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PGSS_DUMP_FILE

#define PGSS_DUMP_FILE   PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"

Definition at line 88 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 311 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 112 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 113 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 111 of file pg_stat_statements.c.

Referenced by entry_dealloc().

◆ USAGE_EXEC

#define USAGE_EXEC (   duration)    (1.0)

Definition at line 107 of file pg_stat_statements.c.

Referenced by pgss_store().

◆ USAGE_INIT

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

Definition at line 108 of file pg_stat_statements.c.

Referenced by entry_alloc(), and pgss_store().

Typedef Documentation

◆ Counters

typedef struct Counters Counters

◆ pgssEntry

typedef struct pgssEntry pgssEntry

◆ pgssHashKey

typedef struct pgssHashKey pgssHashKey

◆ pgssJumbleState

◆ pgssLocationLen

◆ pgssSharedState

◆ pgssStoreKind

◆ pgssVersion

typedef enum pgssVersion pgssVersion

Enumeration Type Documentation

◆ pgssStoreKind

Enumerator
PGSS_INVALID 
PGSS_PLAN 
PGSS_EXEC 
PGSS_NUMKIND 

Definition at line 130 of file pg_stat_statements.c.

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

◆ PGSSTrackLevel

Enumerator
PGSS_TRACK_NONE 
PGSS_TRACK_TOP 
PGSS_TRACK_ALL 

Definition at line 285 of file pg_stat_statements.c.

286 {
287  PGSS_TRACK_NONE, /* track no statements */
288  PGSS_TRACK_TOP, /* only top level statements */
289  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 121 of file pg_stat_statements.c.

Function Documentation

◆ _PG_fini()

void _PG_fini ( void  )

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

499 {
500  /* Uninstall hooks. */
509 }
static ProcessUtility_hook_type prev_ProcessUtility
static ExecutorRun_hook_type prev_ExecutorRun
static ExecutorEnd_hook_type prev_ExecutorEnd
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c:75
static ExecutorStart_hook_type prev_ExecutorStart
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
ExecutorStart_hook_type ExecutorStart_hook
Definition: execMain.c:70
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:52
static shmem_startup_hook_type prev_shmem_startup_hook
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:72
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:51

◆ _PG_init()

void _PG_init ( void  )

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

390 {
391  /*
392  * In order to create our shared memory area, we have to be loaded via
393  * shared_preload_libraries. If not, fall out without hooking into any of
394  * the main system. (We don't throw error here because it seems useful to
395  * allow the pg_stat_statements functions to be created even when the
396  * module isn't active. The functions must protect themselves against
397  * being called then, however.)
398  */
400  return;
401 
402  /*
403  * Define (or redefine) custom GUC variables.
404  */
405  DefineCustomIntVariable("pg_stat_statements.max",
406  "Sets the maximum number of statements tracked by pg_stat_statements.",
407  NULL,
408  &pgss_max,
409  5000,
410  100,
411  INT_MAX,
413  0,
414  NULL,
415  NULL,
416  NULL);
417 
418  DefineCustomEnumVariable("pg_stat_statements.track",
419  "Selects which statements are tracked by pg_stat_statements.",
420  NULL,
421  &pgss_track,
424  PGC_SUSET,
425  0,
426  NULL,
427  NULL,
428  NULL);
429 
430  DefineCustomBoolVariable("pg_stat_statements.track_utility",
431  "Selects whether utility commands are tracked by pg_stat_statements.",
432  NULL,
434  true,
435  PGC_SUSET,
436  0,
437  NULL,
438  NULL,
439  NULL);
440 
441  DefineCustomBoolVariable("pg_stat_statements.track_planning",
442  "Selects whether planning duration is tracked by pg_stat_statements.",
443  NULL,
445  true,
446  PGC_SUSET,
447  0,
448  NULL,
449  NULL,
450  NULL);
451 
452  DefineCustomBoolVariable("pg_stat_statements.save",
453  "Save pg_stat_statements statistics across server shutdowns.",
454  NULL,
455  &pgss_save,
456  true,
457  PGC_SIGHUP,
458  0,
459  NULL,
460  NULL,
461  NULL);
462 
463  EmitWarningsOnPlaceholders("pg_stat_statements");
464 
465  /*
466  * Request additional shared resources. (These are no-ops if we're not in
467  * the postmaster process.) We'll allocate or attach to the shared
468  * resources in pgss_shmem_startup().
469  */
471  RequestNamedLWLockTranche("pg_stat_statements", 1);
472 
473  /*
474  * Install hooks.
475  */
492 }
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:8818
void RequestAddinShmemSpace(Size size)
Definition: ipci.c:70
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1568
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
Definition: lwlock.c:705
static ProcessUtility_hook_type prev_ProcessUtility
static void pgss_shmem_startup(void)
static ExecutorRun_hook_type prev_ExecutorRun
static ExecutorEnd_hook_type prev_ExecutorEnd
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c:75
static int pgss_track
static ExecutorStart_hook_type prev_ExecutorStart
static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
void DefineCustomEnumVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, int bootValue, const struct config_enum_entry *options, GucContext context, int flags, GucEnumCheckHook check_hook, GucEnumAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:8903
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:8931
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:52
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:8792
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:51
static const struct config_enum_entry track_options[]
static bool pgss_save

◆ AppendJumble()

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

Definition at line 2542 of file pg_stat_statements.c.

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

2543 {
2544  unsigned char *jumble = jstate->jumble;
2545  Size jumble_len = jstate->jumble_len;
2546 
2547  /*
2548  * Whenever the jumble buffer is full, we hash the current contents and
2549  * reset the buffer to contain just that hash value, thus relying on the
2550  * hash to summarize everything so far.
2551  */
2552  while (size > 0)
2553  {
2554  Size part_size;
2555 
2556  if (jumble_len >= JUMBLE_SIZE)
2557  {
2558  uint64 start_hash;
2559 
2560  start_hash = DatumGetUInt64(hash_any_extended(jumble,
2561  JUMBLE_SIZE, 0));
2562  memcpy(jumble, &start_hash, sizeof(start_hash));
2563  jumble_len = sizeof(start_hash);
2564  }
2565  part_size = Min(size, JUMBLE_SIZE - jumble_len);
2566  memcpy(jumble + jumble_len, item, part_size);
2567  jumble_len += part_size;
2568  item += part_size;
2569  size -= part_size;
2570  }
2571  jstate->jumble_len = jumble_len;
2572 }
#define Min(x, y)
Definition: c.h:920
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:466

◆ comp_location()

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

Definition at line 3436 of file pg_stat_statements.c.

Referenced by fill_in_constant_lengths().

3437 {
3438  int l = ((const pgssLocationLen *) a)->location;
3439  int r = ((const pgssLocationLen *) b)->location;
3440 
3441  if (l < r)
3442  return -1;
3443  else if (l > r)
3444  return +1;
3445  else
3446  return 0;
3447 }

◆ entry_alloc()

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

Definition at line 1890 of file pg_stat_statements.c.

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

Referenced by pgss_shmem_startup(), and pgss_store().

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

◆ entry_cmp()

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

Definition at line 1927 of file pg_stat_statements.c.

Referenced by entry_dealloc().

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

◆ entry_dealloc()

static void entry_dealloc ( void  )
static

Definition at line 1946 of file pg_stat_statements.c.

References ASSUMED_LENGTH_INIT, pgssEntry::counters, pgssSharedState::cur_median_usage, 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, palloc(), pfree(), qsort, pgssEntry::query_len, STICKY_DECREASE_FACTOR, Counters::usage, USAGE_DEALLOC_PERCENT, and USAGE_DECREASE_FACTOR.

Referenced by entry_alloc().

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

◆ entry_reset()

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

Definition at line 2446 of file pg_stat_statements.c.

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

Referenced by pg_stat_statements_reset(), and pg_stat_statements_reset_1_7().

2447 {
2448  HASH_SEQ_STATUS hash_seq;
2449  pgssEntry *entry;
2450  FILE *qfile;
2451  long num_entries;
2452  long num_remove = 0;
2453  pgssHashKey key;
2454 
2455  if (!pgss || !pgss_hash)
2456  ereport(ERROR,
2457  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2458  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
2459 
2461  num_entries = hash_get_num_entries(pgss_hash);
2462 
2463  if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
2464  {
2465  /* If all the parameters are available, use the fast path. */
2466  key.userid = userid;
2467  key.dbid = dbid;
2468  key.queryid = queryid;
2469 
2470  /* Remove the key if exists */
2471  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
2472  if (entry) /* found */
2473  num_remove++;
2474  }
2475  else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
2476  {
2477  /* Remove entries corresponding to valid parameters. */
2478  hash_seq_init(&hash_seq, pgss_hash);
2479  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2480  {
2481  if ((!userid || entry->key.userid == userid) &&
2482  (!dbid || entry->key.dbid == dbid) &&
2483  (!queryid || entry->key.queryid == queryid))
2484  {
2485  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2486  num_remove++;
2487  }
2488  }
2489  }
2490  else
2491  {
2492  /* Remove all entries. */
2493  hash_seq_init(&hash_seq, pgss_hash);
2494  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2495  {
2496  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2497  num_remove++;
2498  }
2499  }
2500 
2501  /* All entries are removed? */
2502  if (num_entries != num_remove)
2503  goto release_lock;
2504 
2505  /*
2506  * Write new empty query file, perhaps even creating a new one to recover
2507  * if the file was missing.
2508  */
2510  if (qfile == NULL)
2511  {
2512  ereport(LOG,
2514  errmsg("could not create file \"%s\": %m",
2515  PGSS_TEXT_FILE)));
2516  goto done;
2517  }
2518 
2519  /* If ftruncate fails, log it, but it's not a fatal problem */
2520  if (ftruncate(fileno(qfile), 0) != 0)
2521  ereport(LOG,
2523  errmsg("could not truncate file \"%s\": %m",
2524  PGSS_TEXT_FILE)));
2525 
2526  FreeFile(qfile);
2527 
2528 done:
2529  pgss->extent = 0;
2530  /* This counts as a query text garbage collection for our purposes */
2531  record_gc_qtexts();
2532 
2533 release_lock:
2535 }
int errcode(int sqlerrcode)
Definition: elog.c:610
#define PG_BINARY_W
Definition: c.h:1237
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1337
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:908
#define LOG
Definition: elog.h:26
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1812
static pgssSharedState * pgss
static HTAB * pgss_hash
#define ERROR
Definition: elog.h:43
int errcode_for_file_access(void)
Definition: elog.c:633
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2320
pgssHashKey key
#define ereport(elevel,...)
Definition: elog.h:144
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1208
#define record_gc_qtexts()
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1391
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1381
int FreeFile(FILE *file)
Definition: fd.c:2519
int errmsg(const char *fmt,...)
Definition: elog.c:824
#define PGSS_TEXT_FILE
#define ftruncate(a, b)
Definition: win32_port.h:59

◆ fill_in_constant_lengths()

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

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

3338 {
3339  pgssLocationLen *locs;
3341  core_yy_extra_type yyextra;
3342  core_YYSTYPE yylval;
3343  YYLTYPE yylloc;
3344  int last_loc = -1;
3345  int i;
3346 
3347  /*
3348  * Sort the records by location so that we can process them in order while
3349  * scanning the query text.
3350  */
3351  if (jstate->clocations_count > 1)
3352  qsort(jstate->clocations, jstate->clocations_count,
3353  sizeof(pgssLocationLen), comp_location);
3354  locs = jstate->clocations;
3355 
3356  /* initialize the flex scanner --- should match raw_parser() */
3357  yyscanner = scanner_init(query,
3358  &yyextra,
3359  &ScanKeywords,
3361 
3362  /* we don't want to re-emit any escape string warnings */
3363  yyextra.escape_string_warning = false;
3364 
3365  /* Search for each constant, in sequence */
3366  for (i = 0; i < jstate->clocations_count; i++)
3367  {
3368  int loc = locs[i].location;
3369  int tok;
3370 
3371  /* Adjust recorded location if we're dealing with partial string */
3372  loc -= query_loc;
3373 
3374  Assert(loc >= 0);
3375 
3376  if (loc <= last_loc)
3377  continue; /* Duplicate constant, ignore */
3378 
3379  /* Lex tokens until we find the desired constant */
3380  for (;;)
3381  {
3382  tok = core_yylex(&yylval, &yylloc, yyscanner);
3383 
3384  /* We should not hit end-of-string, but if we do, behave sanely */
3385  if (tok == 0)
3386  break; /* out of inner for-loop */
3387 
3388  /*
3389  * We should find the token position exactly, but if we somehow
3390  * run past it, work with that.
3391  */
3392  if (yylloc >= loc)
3393  {
3394  if (query[loc] == '-')
3395  {
3396  /*
3397  * It's a negative value - this is the one and only case
3398  * where we replace more than a single token.
3399  *
3400  * Do not compensate for the core system's special-case
3401  * adjustment of location to that of the leading '-'
3402  * operator in the event of a negative constant. It is
3403  * also useful for our purposes to start from the minus
3404  * symbol. In this way, queries like "select * from foo
3405  * where bar = 1" and "select * from foo where bar = -2"
3406  * will have identical normalized query strings.
3407  */
3408  tok = core_yylex(&yylval, &yylloc, yyscanner);
3409  if (tok == 0)
3410  break; /* out of inner for-loop */
3411  }
3412 
3413  /*
3414  * We now rely on the assumption that flex has placed a zero
3415  * byte after the text of the current token in scanbuf.
3416  */
3417  locs[i].length = strlen(yyextra.scanbuf + loc);
3418  break; /* out of inner for-loop */
3419  }
3420  }
3421 
3422  /* If we hit end-of-string, give up, leaving remaining lengths -1 */
3423  if (tok == 0)
3424  break;
3425 
3426  last_loc = loc;
3427  }
3428 
3429  scanner_finish(yyscanner);
3430 }
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:738
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:479

◆ gc_qtexts()

static void gc_qtexts ( void  )
static

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

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

◆ generate_normalized_query()

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

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

3231 {
3232  char *norm_query;
3233  int query_len = *query_len_p;
3234  int i,
3235  norm_query_buflen, /* Space allowed for norm_query */
3236  len_to_wrt, /* Length (in bytes) to write */
3237  quer_loc = 0, /* Source query byte location */
3238  n_quer_loc = 0, /* Normalized query byte location */
3239  last_off = 0, /* Offset from start for previous tok */
3240  last_tok_len = 0; /* Length (in bytes) of that tok */
3241 
3242  /*
3243  * Get constants' lengths (core system only gives us locations). Note
3244  * this also ensures the items are sorted by location.
3245  */
3246  fill_in_constant_lengths(jstate, query, query_loc);
3247 
3248  /*
3249  * Allow for $n symbols to be longer than the constants they replace.
3250  * Constants must take at least one byte in text form, while a $n symbol
3251  * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We
3252  * could refine that limit based on the max value of n for the current
3253  * query, but it hardly seems worth any extra effort to do so.
3254  */
3255  norm_query_buflen = query_len + jstate->clocations_count * 10;
3256 
3257  /* Allocate result buffer */
3258  norm_query = palloc(norm_query_buflen + 1);
3259 
3260  for (i = 0; i < jstate->clocations_count; i++)
3261  {
3262  int off, /* Offset from start for cur tok */
3263  tok_len; /* Length (in bytes) of that tok */
3264 
3265  off = jstate->clocations[i].location;
3266  /* Adjust recorded location if we're dealing with partial string */
3267  off -= query_loc;
3268 
3269  tok_len = jstate->clocations[i].length;
3270 
3271  if (tok_len < 0)
3272  continue; /* ignore any duplicates */
3273 
3274  /* Copy next chunk (what precedes the next constant) */
3275  len_to_wrt = off - last_off;
3276  len_to_wrt -= last_tok_len;
3277 
3278  Assert(len_to_wrt >= 0);
3279  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
3280  n_quer_loc += len_to_wrt;
3281 
3282  /* And insert a param symbol in place of the constant token */
3283  n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
3284  i + 1 + jstate->highest_extern_param_id);
3285 
3286  quer_loc = off + tok_len;
3287  last_off = off;
3288  last_tok_len = tok_len;
3289  }
3290 
3291  /*
3292  * We've copied up until the last ignorable constant. Copy over the
3293  * remaining bytes of the original query string.
3294  */
3295  len_to_wrt = query_len - quer_loc;
3296 
3297  Assert(len_to_wrt >= 0);
3298  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
3299  n_quer_loc += len_to_wrt;
3300 
3301  Assert(n_quer_loc <= norm_query_buflen);
3302  norm_query[n_quer_loc] = '\0';
3303 
3304  *query_len_p = n_quer_loc;
3305  return norm_query;
3306 }
#define sprintf
Definition: port.h:195
pgssLocationLen * clocations
static void fill_in_constant_lengths(pgssJumbleState *jstate, const char *query, int query_loc)
#define Assert(condition)
Definition: c.h:738
void * palloc(Size size)
Definition: mcxt.c:949
int i

◆ JumbleExpr()

static void JumbleExpr ( pgssJumbleState jstate,
Node node 
)
static

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

2710 {
2711  ListCell *temp;
2712 
2713  if (node == NULL)
2714  return;
2715 
2716  /* Guard against stack overflow due to overly complex expressions */
2718 
2719  /*
2720  * We always emit the node's NodeTag, then any additional fields that are
2721  * considered significant, and then we recurse to any child nodes.
2722  */
2723  APP_JUMB(node->type);
2724 
2725  switch (nodeTag(node))
2726  {
2727  case T_Var:
2728  {
2729  Var *var = (Var *) node;
2730 
2731  APP_JUMB(var->varno);
2732  APP_JUMB(var->varattno);
2733  APP_JUMB(var->varlevelsup);
2734  }
2735  break;
2736  case T_Const:
2737  {
2738  Const *c = (Const *) node;
2739 
2740  /* We jumble only the constant's type, not its value */
2741  APP_JUMB(c->consttype);
2742  /* Also, record its parse location for query normalization */
2743  RecordConstLocation(jstate, c->location);
2744  }
2745  break;
2746  case T_Param:
2747  {
2748  Param *p = (Param *) node;
2749 
2750  APP_JUMB(p->paramkind);
2751  APP_JUMB(p->paramid);
2752  APP_JUMB(p->paramtype);
2753  /* Also, track the highest external Param id */
2754  if (p->paramkind == PARAM_EXTERN &&
2755  p->paramid > jstate->highest_extern_param_id)
2756  jstate->highest_extern_param_id = p->paramid;
2757  }
2758  break;
2759  case T_Aggref:
2760  {
2761  Aggref *expr = (Aggref *) node;
2762 
2763  APP_JUMB(expr->aggfnoid);
2764  JumbleExpr(jstate, (Node *) expr->aggdirectargs);
2765  JumbleExpr(jstate, (Node *) expr->args);
2766  JumbleExpr(jstate, (Node *) expr->aggorder);
2767  JumbleExpr(jstate, (Node *) expr->aggdistinct);
2768  JumbleExpr(jstate, (Node *) expr->aggfilter);
2769  }
2770  break;
2771  case T_GroupingFunc:
2772  {
2773  GroupingFunc *grpnode = (GroupingFunc *) node;
2774 
2775  JumbleExpr(jstate, (Node *) grpnode->refs);
2776  }
2777  break;
2778  case T_WindowFunc:
2779  {
2780  WindowFunc *expr = (WindowFunc *) node;
2781 
2782  APP_JUMB(expr->winfnoid);
2783  APP_JUMB(expr->winref);
2784  JumbleExpr(jstate, (Node *) expr->args);
2785  JumbleExpr(jstate, (Node *) expr->aggfilter);
2786  }
2787  break;
2788  case T_SubscriptingRef:
2789  {
2790  SubscriptingRef *sbsref = (SubscriptingRef *) node;
2791 
2792  JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
2793  JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
2794  JumbleExpr(jstate, (Node *) sbsref->refexpr);
2795  JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
2796  }
2797  break;
2798  case T_FuncExpr:
2799  {
2800  FuncExpr *expr = (FuncExpr *) node;
2801 
2802  APP_JUMB(expr->funcid);
2803  JumbleExpr(jstate, (Node *) expr->args);
2804  }
2805  break;
2806  case T_NamedArgExpr:
2807  {
2808  NamedArgExpr *nae = (NamedArgExpr *) node;
2809 
2810  APP_JUMB(nae->argnumber);
2811  JumbleExpr(jstate, (Node *) nae->arg);
2812  }
2813  break;
2814  case T_OpExpr:
2815  case T_DistinctExpr: /* struct-equivalent to OpExpr */
2816  case T_NullIfExpr: /* struct-equivalent to OpExpr */
2817  {
2818  OpExpr *expr = (OpExpr *) node;
2819 
2820  APP_JUMB(expr->opno);
2821  JumbleExpr(jstate, (Node *) expr->args);
2822  }
2823  break;
2824  case T_ScalarArrayOpExpr:
2825  {
2826  ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
2827 
2828  APP_JUMB(expr->opno);
2829  APP_JUMB(expr->useOr);
2830  JumbleExpr(jstate, (Node *) expr->args);
2831  }
2832  break;
2833  case T_BoolExpr:
2834  {
2835  BoolExpr *expr = (BoolExpr *) node;
2836 
2837  APP_JUMB(expr->boolop);
2838  JumbleExpr(jstate, (Node *) expr->args);
2839  }
2840  break;
2841  case T_SubLink:
2842  {
2843  SubLink *sublink = (SubLink *) node;
2844 
2845  APP_JUMB(sublink->subLinkType);
2846  APP_JUMB(sublink->subLinkId);
2847  JumbleExpr(jstate, (Node *) sublink->testexpr);
2848  JumbleQuery(jstate, castNode(Query, sublink->subselect));
2849  }
2850  break;
2851  case T_FieldSelect:
2852  {
2853  FieldSelect *fs = (FieldSelect *) node;
2854 
2855  APP_JUMB(fs->fieldnum);
2856  JumbleExpr(jstate, (Node *) fs->arg);
2857  }
2858  break;
2859  case T_FieldStore:
2860  {
2861  FieldStore *fstore = (FieldStore *) node;
2862 
2863  JumbleExpr(jstate, (Node *) fstore->arg);
2864  JumbleExpr(jstate, (Node *) fstore->newvals);
2865  }
2866  break;
2867  case T_RelabelType:
2868  {
2869  RelabelType *rt = (RelabelType *) node;
2870 
2871  APP_JUMB(rt->resulttype);
2872  JumbleExpr(jstate, (Node *) rt->arg);
2873  }
2874  break;
2875  case T_CoerceViaIO:
2876  {
2877  CoerceViaIO *cio = (CoerceViaIO *) node;
2878 
2879  APP_JUMB(cio->resulttype);
2880  JumbleExpr(jstate, (Node *) cio->arg);
2881  }
2882  break;
2883  case T_ArrayCoerceExpr:
2884  {
2885  ArrayCoerceExpr *acexpr = (ArrayCoerceExpr *) node;
2886 
2887  APP_JUMB(acexpr->resulttype);
2888  JumbleExpr(jstate, (Node *) acexpr->arg);
2889  JumbleExpr(jstate, (Node *) acexpr->elemexpr);
2890  }
2891  break;
2892  case T_ConvertRowtypeExpr:
2893  {
2894  ConvertRowtypeExpr *crexpr = (ConvertRowtypeExpr *) node;
2895 
2896  APP_JUMB(crexpr->resulttype);
2897  JumbleExpr(jstate, (Node *) crexpr->arg);
2898  }
2899  break;
2900  case T_CollateExpr:
2901  {
2902  CollateExpr *ce = (CollateExpr *) node;
2903 
2904  APP_JUMB(ce->collOid);
2905  JumbleExpr(jstate, (Node *) ce->arg);
2906  }
2907  break;
2908  case T_CaseExpr:
2909  {
2910  CaseExpr *caseexpr = (CaseExpr *) node;
2911 
2912  JumbleExpr(jstate, (Node *) caseexpr->arg);
2913  foreach(temp, caseexpr->args)
2914  {
2915  CaseWhen *when = lfirst_node(CaseWhen, temp);
2916 
2917  JumbleExpr(jstate, (Node *) when->expr);
2918  JumbleExpr(jstate, (Node *) when->result);
2919  }
2920  JumbleExpr(jstate, (Node *) caseexpr->defresult);
2921  }
2922  break;
2923  case T_CaseTestExpr:
2924  {
2925  CaseTestExpr *ct = (CaseTestExpr *) node;
2926 
2927  APP_JUMB(ct->typeId);
2928  }
2929  break;
2930  case T_ArrayExpr:
2931  JumbleExpr(jstate, (Node *) ((ArrayExpr *) node)->elements);
2932  break;
2933  case T_RowExpr:
2934  JumbleExpr(jstate, (Node *) ((RowExpr *) node)->args);
2935  break;
2936  case T_RowCompareExpr:
2937  {
2938  RowCompareExpr *rcexpr = (RowCompareExpr *) node;
2939 
2940  APP_JUMB(rcexpr->rctype);
2941  JumbleExpr(jstate, (Node *) rcexpr->largs);
2942  JumbleExpr(jstate, (Node *) rcexpr->rargs);
2943  }
2944  break;
2945  case T_CoalesceExpr:
2946  JumbleExpr(jstate, (Node *) ((CoalesceExpr *) node)->args);
2947  break;
2948  case T_MinMaxExpr:
2949  {
2950  MinMaxExpr *mmexpr = (MinMaxExpr *) node;
2951 
2952  APP_JUMB(mmexpr->op);
2953  JumbleExpr(jstate, (Node *) mmexpr->args);
2954  }
2955  break;
2956  case T_SQLValueFunction:
2957  {
2958  SQLValueFunction *svf = (SQLValueFunction *) node;
2959 
2960  APP_JUMB(svf->op);
2961  /* type is fully determined by op */
2962  APP_JUMB(svf->typmod);
2963  }
2964  break;
2965  case T_XmlExpr:
2966  {
2967  XmlExpr *xexpr = (XmlExpr *) node;
2968 
2969  APP_JUMB(xexpr->op);
2970  JumbleExpr(jstate, (Node *) xexpr->named_args);
2971  JumbleExpr(jstate, (Node *) xexpr->args);
2972  }
2973  break;
2974  case T_NullTest:
2975  {
2976  NullTest *nt = (NullTest *) node;
2977 
2978  APP_JUMB(nt->nulltesttype);
2979  JumbleExpr(jstate, (Node *) nt->arg);
2980  }
2981  break;
2982  case T_BooleanTest:
2983  {
2984  BooleanTest *bt = (BooleanTest *) node;
2985 
2986  APP_JUMB(bt->booltesttype);
2987  JumbleExpr(jstate, (Node *) bt->arg);
2988  }
2989  break;
2990  case T_CoerceToDomain:
2991  {
2992  CoerceToDomain *cd = (CoerceToDomain *) node;
2993 
2994  APP_JUMB(cd->resulttype);
2995  JumbleExpr(jstate, (Node *) cd->arg);
2996  }
2997  break;
2998  case T_CoerceToDomainValue:
2999  {
3000  CoerceToDomainValue *cdv = (CoerceToDomainValue *) node;
3001 
3002  APP_JUMB(cdv->typeId);
3003  }
3004  break;
3005  case T_SetToDefault:
3006  {
3007  SetToDefault *sd = (SetToDefault *) node;
3008 
3009  APP_JUMB(sd->typeId);
3010  }
3011  break;
3012  case T_CurrentOfExpr:
3013  {
3014  CurrentOfExpr *ce = (CurrentOfExpr *) node;
3015 
3016  APP_JUMB(ce->cvarno);
3017  if (ce->cursor_name)
3019  APP_JUMB(ce->cursor_param);
3020  }
3021  break;
3022  case T_NextValueExpr:
3023  {
3024  NextValueExpr *nve = (NextValueExpr *) node;
3025 
3026  APP_JUMB(nve->seqid);
3027  APP_JUMB(nve->typeId);
3028  }
3029  break;
3030  case T_InferenceElem:
3031  {
3032  InferenceElem *ie = (InferenceElem *) node;
3033 
3034  APP_JUMB(ie->infercollid);
3035  APP_JUMB(ie->inferopclass);
3036  JumbleExpr(jstate, ie->expr);
3037  }
3038  break;
3039  case T_TargetEntry:
3040  {
3041  TargetEntry *tle = (TargetEntry *) node;
3042 
3043  APP_JUMB(tle->resno);
3044  APP_JUMB(tle->ressortgroupref);
3045  JumbleExpr(jstate, (Node *) tle->expr);
3046  }
3047  break;
3048  case T_RangeTblRef:
3049  {
3050  RangeTblRef *rtr = (RangeTblRef *) node;
3051 
3052  APP_JUMB(rtr->rtindex);
3053  }
3054  break;
3055  case T_JoinExpr:
3056  {
3057  JoinExpr *join = (JoinExpr *) node;
3058 
3059  APP_JUMB(join->jointype);
3060  APP_JUMB(join->isNatural);
3061  APP_JUMB(join->rtindex);
3062  JumbleExpr(jstate, join->larg);
3063  JumbleExpr(jstate, join->rarg);
3064  JumbleExpr(jstate, join->quals);
3065  }
3066  break;
3067  case T_FromExpr:
3068  {
3069  FromExpr *from = (FromExpr *) node;
3070 
3071  JumbleExpr(jstate, (Node *) from->fromlist);
3072  JumbleExpr(jstate, from->quals);
3073  }
3074  break;
3075  case T_OnConflictExpr:
3076  {
3077  OnConflictExpr *conf = (OnConflictExpr *) node;
3078 
3079  APP_JUMB(conf->action);
3080  JumbleExpr(jstate, (Node *) conf->arbiterElems);
3081  JumbleExpr(jstate, conf->arbiterWhere);
3082  JumbleExpr(jstate, (Node *) conf->onConflictSet);
3083  JumbleExpr(jstate, conf->onConflictWhere);
3084  APP_JUMB(conf->constraint);
3085  APP_JUMB(conf->exclRelIndex);
3086  JumbleExpr(jstate, (Node *) conf->exclRelTlist);
3087  }
3088  break;
3089  case T_List:
3090  foreach(temp, (List *) node)
3091  {
3092  JumbleExpr(jstate, (Node *) lfirst(temp));
3093  }
3094  break;
3095  case T_IntList:
3096  foreach(temp, (List *) node)
3097  {
3098  APP_JUMB(lfirst_int(temp));
3099  }
3100  break;
3101  case T_SortGroupClause:
3102  {
3103  SortGroupClause *sgc = (SortGroupClause *) node;
3104 
3105  APP_JUMB(sgc->tleSortGroupRef);
3106  APP_JUMB(sgc->eqop);
3107  APP_JUMB(sgc->sortop);
3108  APP_JUMB(sgc->nulls_first);
3109  }
3110  break;
3111  case T_GroupingSet:
3112  {
3113  GroupingSet *gsnode = (GroupingSet *) node;
3114 
3115  JumbleExpr(jstate, (Node *) gsnode->content);
3116  }
3117  break;
3118  case T_WindowClause:
3119  {
3120  WindowClause *wc = (WindowClause *) node;
3121 
3122  APP_JUMB(wc->winref);
3123  APP_JUMB(wc->frameOptions);
3124  JumbleExpr(jstate, (Node *) wc->partitionClause);
3125  JumbleExpr(jstate, (Node *) wc->orderClause);
3126  JumbleExpr(jstate, wc->startOffset);
3127  JumbleExpr(jstate, wc->endOffset);
3128  }
3129  break;
3130  case T_CommonTableExpr:
3131  {
3132  CommonTableExpr *cte = (CommonTableExpr *) node;
3133 
3134  /* we store the string name because RTE_CTE RTEs need it */
3135  APP_JUMB_STRING(cte->ctename);
3136  APP_JUMB(cte->ctematerialized);
3137  JumbleQuery(jstate, castNode(Query, cte->ctequery));
3138  }
3139  break;
3140  case T_SetOperationStmt:
3141  {
3142  SetOperationStmt *setop = (SetOperationStmt *) node;
3143 
3144  APP_JUMB(setop->op);
3145  APP_JUMB(setop->all);
3146  JumbleExpr(jstate, setop->larg);
3147  JumbleExpr(jstate, setop->rarg);
3148  }
3149  break;
3150  case T_RangeTblFunction:
3151  {
3152  RangeTblFunction *rtfunc = (RangeTblFunction *) node;
3153 
3154  JumbleExpr(jstate, rtfunc->funcexpr);
3155  }
3156  break;
3157  case T_TableFunc:
3158  {
3159  TableFunc *tablefunc = (TableFunc *) node;
3160 
3161  JumbleExpr(jstate, tablefunc->docexpr);
3162  JumbleExpr(jstate, tablefunc->rowexpr);
3163  JumbleExpr(jstate, (Node *) tablefunc->colexprs);
3164  }
3165  break;
3166  case T_TableSampleClause:
3167  {
3168  TableSampleClause *tsc = (TableSampleClause *) node;
3169 
3170  APP_JUMB(tsc->tsmhandler);
3171  JumbleExpr(jstate, (Node *) tsc->args);
3172  JumbleExpr(jstate, (Node *) tsc->repeatable);
3173  }
3174  break;
3175  default:
3176  /* Only a warning, since we can stumble along anyway */
3177  elog(WARNING, "unrecognized node type: %d",
3178  (int) nodeTag(node));
3179  break;
3180  }
3181 }
List * aggdistinct
Definition: primnodes.h:321
List * args
Definition: primnodes.h:1106
static void JumbleExpr(pgssJumbleState *jstate, Node *node)
Node * docexpr
Definition: primnodes.h:87
Expr * arg
Definition: primnodes.h:791
Index varlevelsup
Definition: primnodes.h:191
List * content
Definition: parsenodes.h:1327
List * refs
Definition: primnodes.h:361
List * args
Definition: primnodes.h:377
List * args
Definition: primnodes.h:477
#define castNode(_type_, nodeptr)
Definition: nodes.h:598
Oid resulttype
Definition: primnodes.h:835
RowCompareType rctype
Definition: primnodes.h:1070
Index tleSortGroupRef
Definition: parsenodes.h:1257
Expr * arg
Definition: primnodes.h:814
ParamKind paramkind
Definition: primnodes.h:262
Definition: nodes.h:529
List * args
Definition: primnodes.h:319
AttrNumber varattno
Definition: primnodes.h:186
Expr * arg
Definition: primnodes.h:762
List * fromlist
Definition: primnodes.h:1510
Index winref
Definition: primnodes.h:379
Definition: primnodes.h:181
List * refupperindexpr
Definition: primnodes.h:422
Node * quals
Definition: primnodes.h:1511
SQLValueFunctionOp op
Definition: primnodes.h:1143
#define APP_JUMB_STRING(str)
static void RecordConstLocation(pgssJumbleState *jstate, int location)
List * arbiterElems
Definition: primnodes.h:1529
Node * larg
Definition: primnodes.h:1490
Oid consttype
Definition: primnodes.h:210
Oid funcid
Definition: primnodes.h:469
#define lfirst_int(lc)
Definition: pg_list.h:191
List * partitionClause
Definition: parsenodes.h:1353
BoolExprType boolop
Definition: primnodes.h:582
Expr * arg
Definition: primnodes.h:1219
#define lfirst_node(type, lc)
Definition: pg_list.h:193
#define APP_JUMB(item)
Node * rowexpr
Definition: primnodes.h:88
char * c
NodeTag type
Definition: nodes.h:531
static void JumbleQuery(pgssJumbleState *jstate, Query *query)
List * exclRelTlist
Definition: primnodes.h:1538
void check_stack_depth(void)
Definition: postgres.c:3312
List * aggorder
Definition: primnodes.h:320
Expr * arg
Definition: primnodes.h:1242
AttrNumber resno
Definition: primnodes.h:1408
char * cursor_name
Definition: primnodes.h:1317
List * aggdirectargs
Definition: primnodes.h:318
Oid winfnoid
Definition: primnodes.h:373
Expr * arg
Definition: primnodes.h:834
Expr * elemexpr
Definition: primnodes.h:859
Definition: type.h:82
Definition: nodes.h:300
List * newvals
Definition: primnodes.h:792
Definition: nodes.h:154
OnConflictAction action
Definition: primnodes.h:1526
bool isNatural
Definition: primnodes.h:1489
#define WARNING
Definition: elog.h:40
Index varno
Definition: primnodes.h:184
Definition: nodes.h:153
XmlExprOp op
Definition: primnodes.h:1181
Node * startOffset
Definition: parsenodes.h:1356
List * args
Definition: primnodes.h:933
int location
Definition: primnodes.h:221
Node * quals
Definition: primnodes.h:1493
BoolTestType booltesttype
Definition: primnodes.h:1243
Oid resulttype
Definition: primnodes.h:815
NullTestType nulltesttype
Definition: primnodes.h:1220
Oid aggfnoid
Definition: primnodes.h:312
List * colexprs
Definition: primnodes.h:93
List * named_args
Definition: primnodes.h:1183
List * args
Definition: primnodes.h:1185
Node * rarg
Definition: primnodes.h:1491
Expr * arg
Definition: primnodes.h:498
JoinType jointype
Definition: primnodes.h:1488
#define lfirst(lc)
Definition: pg_list.h:190
CTEMaterialize ctematerialized
Definition: parsenodes.h:1452
Expr * aggfilter
Definition: primnodes.h:378
Expr * expr
Definition: primnodes.h:1407
int paramid
Definition: primnodes.h:263
Node * endOffset
Definition: parsenodes.h:1357
Expr * arg
Definition: primnodes.h:900
Expr * aggfilter
Definition: primnodes.h:322
SetOperation op
Definition: parsenodes.h:1658
List * args
Definition: primnodes.h:583
#define nodeTag(nodeptr)
Definition: nodes.h:534
List * orderClause
Definition: parsenodes.h:1354
Node * arbiterWhere
Definition: primnodes.h:1531
Expr * refassgnexpr
Definition: primnodes.h:430
List * reflowerindexpr
Definition: primnodes.h:424
#define elog(elevel,...)
Definition: elog.h:214
List * onConflictSet
Definition: primnodes.h:1535
Index ressortgroupref
Definition: primnodes.h:1410
MinMaxOp op
Definition: primnodes.h:1105
Expr * refexpr
Definition: primnodes.h:427
Expr * arg
Definition: primnodes.h:932
Oid opno
Definition: primnodes.h:516
Expr * result
Definition: primnodes.h:945
List * args
Definition: primnodes.h:522
Expr * defresult
Definition: primnodes.h:934
Expr * expr
Definition: primnodes.h:944
Node * onConflictWhere
Definition: primnodes.h:1536
int rtindex
Definition: primnodes.h:1495
Definition: pg_list.h:50
Oid paramtype
Definition: primnodes.h:264
Definition: nodes.h:155
AttrNumber fieldnum
Definition: primnodes.h:763

◆ JumbleQuery()

static void JumbleQuery ( pgssJumbleState jstate,
Query query 
)
static

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

2594 {
2595  Assert(IsA(query, Query));
2596  Assert(query->utilityStmt == NULL);
2597 
2598  APP_JUMB(query->commandType);
2599  /* resultRelation is usually predictable from commandType */
2600  JumbleExpr(jstate, (Node *) query->cteList);
2601  JumbleRangeTable(jstate, query->rtable);
2602  JumbleExpr(jstate, (Node *) query->jointree);
2603  JumbleExpr(jstate, (Node *) query->targetList);
2604  JumbleExpr(jstate, (Node *) query->onConflict);
2605  JumbleExpr(jstate, (Node *) query->returningList);
2606  JumbleExpr(jstate, (Node *) query->groupClause);
2607  JumbleExpr(jstate, (Node *) query->groupingSets);
2608  JumbleExpr(jstate, query->havingQual);
2609  JumbleExpr(jstate, (Node *) query->windowClause);
2610  JumbleExpr(jstate, (Node *) query->distinctClause);
2611  JumbleExpr(jstate, (Node *) query->sortClause);
2612  JumbleExpr(jstate, query->limitOffset);
2613  JumbleExpr(jstate, query->limitCount);
2614  JumbleRowMarks(jstate, query->rowMarks);
2615  JumbleExpr(jstate, query->setOperations);
2616 }
Node * limitOffset
Definition: parsenodes.h:160
static void JumbleExpr(pgssJumbleState *jstate, Node *node)
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
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:529
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:738
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 2622 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().

2623 {
2624  ListCell *lc;
2625 
2626  foreach(lc, rtable)
2627  {
2629 
2630  APP_JUMB(rte->rtekind);
2631  switch (rte->rtekind)
2632  {
2633  case RTE_RELATION:
2634  APP_JUMB(rte->relid);
2635  JumbleExpr(jstate, (Node *) rte->tablesample);
2636  break;
2637  case RTE_SUBQUERY:
2638  JumbleQuery(jstate, rte->subquery);
2639  break;
2640  case RTE_JOIN:
2641  APP_JUMB(rte->jointype);
2642  break;
2643  case RTE_FUNCTION:
2644  JumbleExpr(jstate, (Node *) rte->functions);
2645  break;
2646  case RTE_TABLEFUNC:
2647  JumbleExpr(jstate, (Node *) rte->tablefunc);
2648  break;
2649  case RTE_VALUES:
2650  JumbleExpr(jstate, (Node *) rte->values_lists);
2651  break;
2652  case RTE_CTE:
2653 
2654  /*
2655  * Depending on the CTE name here isn't ideal, but it's the
2656  * only info we have to identify the referenced WITH item.
2657  */
2658  APP_JUMB_STRING(rte->ctename);
2659  APP_JUMB(rte->ctelevelsup);
2660  break;
2661  case RTE_NAMEDTUPLESTORE:
2662  APP_JUMB_STRING(rte->enrname);
2663  break;
2664  case RTE_RESULT:
2665  break;
2666  default:
2667  elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
2668  break;
2669  }
2670  }
2671 }
static void JumbleExpr(pgssJumbleState *jstate, Node *node)
Definition: nodes.h:529
List * values_lists
Definition: parsenodes.h:1074
#define APP_JUMB_STRING(str)
#define ERROR
Definition: elog.h:43
TableFunc * tablefunc
Definition: parsenodes.h:1069
#define lfirst_node(type, lc)
Definition: pg_list.h:193
#define APP_JUMB(item)
static void JumbleQuery(pgssJumbleState *jstate, Query *query)
JoinType jointype
Definition: parsenodes.h:1049
char * enrname
Definition: parsenodes.h:1108
List * functions
Definition: parsenodes.h:1063
Index ctelevelsup
Definition: parsenodes.h:1080
RTEKind rtekind
Definition: parsenodes.h:976
char * ctename
Definition: parsenodes.h:1079
Query * subquery
Definition: parsenodes.h:1011
#define elog(elevel,...)
Definition: elog.h:214
struct TableSampleClause * tablesample
Definition: parsenodes.h:1006

◆ JumbleRowMarks()

static void JumbleRowMarks ( pgssJumbleState jstate,
List rowMarks 
)
static

Definition at line 2677 of file pg_stat_statements.c.

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

Referenced by JumbleQuery().

2678 {
2679  ListCell *lc;
2680 
2681  foreach(lc, rowMarks)
2682  {
2683  RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
2684 
2685  if (!rowmark->pushedDown)
2686  {
2687  APP_JUMB(rowmark->rti);
2688  APP_JUMB(rowmark->strength);
2689  APP_JUMB(rowmark->waitPolicy);
2690  }
2691  }
2692 }
LockClauseStrength strength
Definition: parsenodes.h:1383
#define lfirst_node(type, lc)
Definition: pg_list.h:193
#define APP_JUMB(item)
LockWaitPolicy waitPolicy
Definition: parsenodes.h:1384

◆ need_gc_qtexts()

static bool need_gc_qtexts ( void  )
static

Definition at line 2215 of file pg_stat_statements.c.

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

Referenced by gc_qtexts(), and pgss_store().

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

◆ PG_FUNCTION_INFO_V1() [1/6]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_reset  )

◆ PG_FUNCTION_INFO_V1() [2/6]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_reset_1_7  )

◆ PG_FUNCTION_INFO_V1() [3/6]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_2  )

◆ PG_FUNCTION_INFO_V1() [4/6]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_3  )

◆ PG_FUNCTION_INFO_V1() [5/6]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_8  )

◆ PG_FUNCTION_INFO_V1() [6/6]

PG_FUNCTION_INFO_V1 ( pg_stat_statements  )

◆ pg_stat_statements()

Datum pg_stat_statements ( PG_FUNCTION_ARGS  )

Definition at line 1532 of file pg_stat_statements.c.

References pg_stat_statements_internal(), and PGSS_V1_0.

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

◆ pg_stat_statements_1_2()

Datum pg_stat_statements_1_2 ( PG_FUNCTION_ARGS  )

Definition at line 1518 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_2.

1519 {
1520  bool showtext = PG_GETARG_BOOL(0);
1521 
1522  pg_stat_statements_internal(fcinfo, PGSS_V1_2, showtext);
1523 
1524  return (Datum) 0;
1525 }
#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 1508 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_3.

1509 {
1510  bool showtext = PG_GETARG_BOOL(0);
1511 
1512  pg_stat_statements_internal(fcinfo, PGSS_V1_3, showtext);
1513 
1514  return (Datum) 0;
1515 }
#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 1498 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_8.

1499 {
1500  bool showtext = PG_GETARG_BOOL(0);
1501 
1502  pg_stat_statements_internal(fcinfo, PGSS_V1_8, showtext);
1503 
1504  return (Datum) 0;
1505 }
#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_internal()

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

Definition at line 1542 of file pg_stat_statements.c.

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

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

References entry_reset(), and PG_RETURN_VOID.

1473 {
1474  entry_reset(0, 0, 0);
1475 
1476  PG_RETURN_VOID();
1477 }
static void entry_reset(Oid userid, Oid dbid, uint64 queryid)
#define PG_RETURN_VOID()
Definition: fmgr.h:348

◆ pg_stat_statements_reset_1_7()

Datum pg_stat_statements_reset_1_7 ( PG_FUNCTION_ARGS  )

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

1454 {
1455  Oid userid;
1456  Oid dbid;
1457  uint64 queryid;
1458 
1459  userid = PG_GETARG_OID(0);
1460  dbid = PG_GETARG_OID(1);
1461  queryid = (uint64) PG_GETARG_INT64(2);
1462 
1463  entry_reset(userid, dbid, queryid);
1464 
1465  PG_RETURN_VOID();
1466 }
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:348
#define PG_GETARG_INT64(n)
Definition: fmgr.h:282

◆ pgss_ExecutorEnd()

static void pgss_ExecutorEnd ( QueryDesc queryDesc)
static

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

1080 {
1081  uint64 queryId = queryDesc->plannedstmt->queryId;
1082 
1083  if (queryId != UINT64CONST(0) && queryDesc->totaltime &&
1085  {
1086  /*
1087  * Make sure stats accumulation is done. (Note: it's okay if several
1088  * levels of hook all do this.)
1089  */
1090  InstrEndLoop(queryDesc->totaltime);
1091 
1092  pgss_store(queryDesc->sourceText,
1093  queryId,
1094  queryDesc->plannedstmt->stmt_location,
1095  queryDesc->plannedstmt->stmt_len,
1096  PGSS_EXEC,
1097  queryDesc->totaltime->total * 1000.0, /* convert to msec */
1098  queryDesc->estate->es_processed,
1099  &queryDesc->totaltime->bufusage,
1100  &queryDesc->totaltime->walusage,
1101  NULL);
1102  }
1103 
1104  if (prev_ExecutorEnd)
1105  prev_ExecutorEnd(queryDesc);
1106  else
1107  standard_ExecutorEnd(queryDesc);
1108 }
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:471
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:96
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:121
int stmt_location
Definition: plannodes.h:95
BufferUsage bufusage
Definition: instrument.h:74
struct Instrumentation * totaltime
Definition: execdesc.h:55
uint64 es_processed
Definition: execnodes.h:559
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 1058 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().

1059 {
1061  PG_TRY();
1062  {
1063  if (prev_ExecutorFinish)
1064  prev_ExecutorFinish(queryDesc);
1065  else
1066  standard_ExecutorFinish(queryDesc);
1067  }
1068  PG_FINALLY();
1069  {
1071  }
1072  PG_END_TRY();
1073 }
static int exec_nested_level
void standard_ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:411
static ExecutorFinish_hook_type prev_ExecutorFinish
#define PG_FINALLY()
Definition: elog.h:312
#define PG_TRY()
Definition: elog.h:295
#define PG_END_TRY()
Definition: elog.h:320

◆ pgss_ExecutorRun()

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

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

1038 {
1040  PG_TRY();
1041  {
1042  if (prev_ExecutorRun)
1043  prev_ExecutorRun(queryDesc, direction, count, execute_once);
1044  else
1045  standard_ExecutorRun(queryDesc, direction, count, execute_once);
1046  }
1047  PG_FINALLY();
1048  {
1050  }
1051  PG_END_TRY();
1052 }
void standard_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:312
static ExecutorRun_hook_type prev_ExecutorRun
static int exec_nested_level
#define PG_FINALLY()
Definition: elog.h:312
#define PG_TRY()
Definition: elog.h:295
#define PG_END_TRY()
Definition: elog.h:320

◆ pgss_ExecutorStart()

static void pgss_ExecutorStart ( QueryDesc queryDesc,
int  eflags 
)
static

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

1003 {
1004  if (prev_ExecutorStart)
1005  prev_ExecutorStart(queryDesc, eflags);
1006  else
1007  standard_ExecutorStart(queryDesc, eflags);
1008 
1009  /*
1010  * If query has queryId zero, don't track it. This prevents double
1011  * counting of optimizable statements that are directly contained in
1012  * utility statements.
1013  */
1014  if (pgss_enabled(exec_nested_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0))
1015  {
1016  /*
1017  * Set up to track total elapsed time in ExecutorRun. Make sure the
1018  * space is allocated in the per-query context so it will go away at
1019  * ExecutorEnd.
1020  */
1021  if (queryDesc->totaltime == NULL)
1022  {
1023  MemoryContext oldcxt;
1024 
1025  oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
1026  queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL);
1027  MemoryContextSwitchTo(oldcxt);
1028  }
1029  }
1030 }
EState * estate
Definition: execdesc.h:48
void standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:152
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#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:555
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 1213 of file pg_stat_statements.c.

References DatumGetUInt64, and hash_any_extended().

Referenced by pgss_store().

1214 {
1215  return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
1216  len, 0));
1217 }
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 1862 of file pg_stat_statements.c.

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

Referenced by _PG_init().

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

◆ pgss_planner()

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

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

911 {
912  PlannedStmt *result;
913 
914  /*
915  * We can't process the query if no query_string is provided, as
916  * pgss_store needs it. We also ignore query without queryid, as it would
917  * be treated as a utility statement, which may not be the case.
918  *
919  * Note that planner_hook can be called from the planner itself, so we
920  * have a specific nesting level for the planner. However, utility
921  * commands containing optimizable statements can also call the planner,
922  * same for regular DML (for instance for underlying foreign key queries).
923  * So testing the planner nesting level only is not enough to detect real
924  * top level planner call.
925  */
927  && pgss_track_planning && query_string
928  && parse->queryId != UINT64CONST(0))
929  {
930  instr_time start;
932  BufferUsage bufusage_start,
933  bufusage;
934  WalUsage walusage_start,
935  walusage;
936 
937  /* We need to track buffer usage as the planner can access them. */
938  bufusage_start = pgBufferUsage;
939 
940  /*
941  * Similarly the planner could write some WAL records in some cases
942  * (e.g. setting a hint bit with those being WAL-logged)
943  */
944  walusage_start = pgWalUsage;
945  INSTR_TIME_SET_CURRENT(start);
946 
948  PG_TRY();
949  {
950  if (prev_planner_hook)
951  result = prev_planner_hook(parse, query_string, cursorOptions,
952  boundParams);
953  else
954  result = standard_planner(parse, query_string, cursorOptions,
955  boundParams);
956  }
957  PG_FINALLY();
958  {
960  }
961  PG_END_TRY();
962 
963  INSTR_TIME_SET_CURRENT(duration);
964  INSTR_TIME_SUBTRACT(duration, start);
965 
966  /* calc differences of buffer counters. */
967  memset(&bufusage, 0, sizeof(BufferUsage));
968  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
969 
970  /* calc differences of WAL counters. */
971  memset(&walusage, 0, sizeof(WalUsage));
972  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
973 
974  pgss_store(query_string,
975  parse->queryId,
976  parse->stmt_location,
977  parse->stmt_len,
978  PGSS_PLAN,
979  INSTR_TIME_GET_MILLISEC(duration),
980  0,
981  &bufusage,
982  &walusage,
983  NULL);
984  }
985  else
986  {
987  if (prev_planner_hook)
988  result = prev_planner_hook(parse, query_string, cursorOptions,
989  boundParams);
990  else
991  result = standard_planner(parse, query_string, cursorOptions,
992  boundParams);
993  }
994 
995  return result;
996 }
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:146
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:312
#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:280
#define PG_TRY()
Definition: elog.h:295
int stmt_len
Definition: parsenodes.h:182
#define PG_END_TRY()
Definition: elog.h:320
BufferUsage pgBufferUsage
Definition: instrument.c:20

◆ pgss_post_parse_analyze()

static void pgss_post_parse_analyze ( ParseState pstate,
Query query 
)
static

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

834 {
835  pgssJumbleState jstate;
836 
838  prev_post_parse_analyze_hook(pstate, query);
839 
840  /* Assert we didn't do this already */
841  Assert(query->queryId == UINT64CONST(0));
842 
843  /* Safety check... */
845  return;
846 
847  /*
848  * Utility statements get queryId zero. We do this even in cases where
849  * the statement contains an optimizable statement for which a queryId
850  * could be derived (such as EXPLAIN or DECLARE CURSOR). For such cases,
851  * runtime control will first go through ProcessUtility and then the
852  * executor, and we don't want the executor hooks to do anything, since we
853  * are already measuring the statement's costs at the utility level.
854  */
855  if (query->utilityStmt)
856  {
857  query->queryId = UINT64CONST(0);
858  return;
859  }
860 
861  /* Set up workspace for query jumbling */
862  jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
863  jstate.jumble_len = 0;
864  jstate.clocations_buf_size = 32;
865  jstate.clocations = (pgssLocationLen *)
866  palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
867  jstate.clocations_count = 0;
868  jstate.highest_extern_param_id = 0;
869 
870  /* Compute query ID and mark the Query node with it */
871  JumbleQuery(&jstate, query);
872  query->queryId =
874 
875  /*
876  * If we are unlucky enough to get a hash of zero, use 1 instead, to
877  * prevent confusion with the utility-statement case.
878  */
879  if (query->queryId == UINT64CONST(0))
880  query->queryId = UINT64CONST(1);
881 
882  /*
883  * If we were able to identify any ignorable constants, we immediately
884  * create a hash table entry for the query, so that we can record the
885  * normalized form of the query string. If there were no such constants,
886  * the normalized string would be the same as the query text anyway, so
887  * there's no need for an early entry.
888  */
889  if (jstate.clocations_count > 0)
890  pgss_store(pstate->p_sourcetext,
891  query->queryId,
892  query->stmt_location,
893  query->stmt_len,
894  PGSS_INVALID,
895  0,
896  0,
897  NULL,
898  NULL,
899  &jstate);
900 }
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:179
uint64 queryId
Definition: parsenodes.h:116
#define DatumGetUInt64(X)
Definition: postgres.h:634
#define Assert(condition)
Definition: c.h:738
void * palloc(Size size)
Definition: mcxt.c:949
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 1114 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().

1118 {
1119  Node *parsetree = pstmt->utilityStmt;
1120 
1121  /*
1122  * If it's an EXECUTE statement, we don't track it and don't increment the
1123  * nesting level. This allows the cycles to be charged to the underlying
1124  * PREPARE instead (by the Executor hooks), which is much more useful.
1125  *
1126  * We also don't track execution of PREPARE. If we did, we would get one
1127  * hash table entry for the PREPARE (with hash calculated from the query
1128  * string), and then a different one with the same query string (but hash
1129  * calculated from the query tree) would be used to accumulate costs of
1130  * ensuing EXECUTEs. This would be confusing, and inconsistent with other
1131  * cases where planning time is not included at all.
1132  *
1133  * Likewise, we don't track execution of DEALLOCATE.
1134  */
1136  !IsA(parsetree, ExecuteStmt) &&
1137  !IsA(parsetree, PrepareStmt) &&
1138  !IsA(parsetree, DeallocateStmt))
1139  {
1140  instr_time start;
1142  uint64 rows;
1143  BufferUsage bufusage_start,
1144  bufusage;
1145  WalUsage walusage_start,
1146  walusage;
1147 
1148  bufusage_start = pgBufferUsage;
1149  walusage_start = pgWalUsage;
1150  INSTR_TIME_SET_CURRENT(start);
1151 
1153  PG_TRY();
1154  {
1155  if (prev_ProcessUtility)
1156  prev_ProcessUtility(pstmt, queryString,
1157  context, params, queryEnv,
1158  dest, qc);
1159  else
1160  standard_ProcessUtility(pstmt, queryString,
1161  context, params, queryEnv,
1162  dest, qc);
1163  }
1164  PG_FINALLY();
1165  {
1167  }
1168  PG_END_TRY();
1169 
1170  INSTR_TIME_SET_CURRENT(duration);
1171  INSTR_TIME_SUBTRACT(duration, start);
1172 
1173  rows = (qc && qc->commandTag == CMDTAG_COPY) ? qc->nprocessed : 0;
1174 
1175  /* calc differences of buffer counters. */
1176  memset(&bufusage, 0, sizeof(BufferUsage));
1177  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
1178 
1179  /* calc differences of WAL counters. */
1180  memset(&walusage, 0, sizeof(WalUsage));
1181  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
1182 
1183  pgss_store(queryString,
1184  0, /* signal that it's a utility stmt */
1185  pstmt->stmt_location,
1186  pstmt->stmt_len,
1187  PGSS_EXEC,
1188  INSTR_TIME_GET_MILLISEC(duration),
1189  rows,
1190  &bufusage,
1191  &walusage,
1192  NULL);
1193  }
1194  else
1195  {
1196  if (prev_ProcessUtility)
1197  prev_ProcessUtility(pstmt, queryString,
1198  context, params, queryEnv,
1199  dest, qc);
1200  else
1201  standard_ProcessUtility(pstmt, queryString,
1202  context, params, queryEnv,
1203  dest, qc);
1204  }
1205 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
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:529
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:96
int duration
Definition: pgbench.c:146
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:541
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
int stmt_location
Definition: plannodes.h:95
Node * utilityStmt
Definition: plannodes.h:92
#define PG_FINALLY()
Definition: elog.h:312
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:295
#define PG_END_TRY()
Definition: elog.h:320
BufferUsage pgBufferUsage
Definition: instrument.c:20

◆ pgss_shmem_shutdown()

static void pgss_shmem_shutdown ( int  code,
Datum  arg 
)
static

Definition at line 737 of file pg_stat_statements.c.

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

Referenced by pgss_shmem_startup().

738 {
739  FILE *file;
740  char *qbuffer = NULL;
741  Size qbuffer_size = 0;
742  HASH_SEQ_STATUS hash_seq;
743  int32 num_entries;
744  pgssEntry *entry;
745 
746  /* Don't try to dump during a crash. */
747  if (code)
748  return;
749 
750  /* Safety check ... shouldn't get here unless shmem is set up. */
751  if (!pgss || !pgss_hash)
752  return;
753 
754  /* Don't dump if told not to. */
755  if (!pgss_save)
756  return;
757 
758  file = AllocateFile(PGSS_DUMP_FILE ".tmp", PG_BINARY_W);
759  if (file == NULL)
760  goto error;
761 
762  if (fwrite(&PGSS_FILE_HEADER, sizeof(uint32), 1, file) != 1)
763  goto error;
764  if (fwrite(&PGSS_PG_MAJOR_VERSION, sizeof(uint32), 1, file) != 1)
765  goto error;
766  num_entries = hash_get_num_entries(pgss_hash);
767  if (fwrite(&num_entries, sizeof(int32), 1, file) != 1)
768  goto error;
769 
770  qbuffer = qtext_load_file(&qbuffer_size);
771  if (qbuffer == NULL)
772  goto error;
773 
774  /*
775  * When serializing to disk, we store query texts immediately after their
776  * entry data. Any orphaned query texts are thereby excluded.
777  */
778  hash_seq_init(&hash_seq, pgss_hash);
779  while ((entry = hash_seq_search(&hash_seq)) != NULL)
780  {
781  int len = entry->query_len;
782  char *qstr = qtext_fetch(entry->query_offset, len,
783  qbuffer, qbuffer_size);
784 
785  if (qstr == NULL)
786  continue; /* Ignore any entries with bogus texts */
787 
788  if (fwrite(entry, sizeof(pgssEntry), 1, file) != 1 ||
789  fwrite(qstr, 1, len + 1, file) != len + 1)
790  {
791  /* note: we assume hash_seq_term won't change errno */
792  hash_seq_term(&hash_seq);
793  goto error;
794  }
795  }
796 
797  free(qbuffer);
798  qbuffer = NULL;
799 
800  if (FreeFile(file))
801  {
802  file = NULL;
803  goto error;
804  }
805 
806  /*
807  * Rename file into place, so we atomically replace any old one.
808  */
810 
811  /* Unlink query-texts file; it's not needed while shutdown */
812  unlink(PGSS_TEXT_FILE);
813 
814  return;
815 
816 error:
817  ereport(LOG,
819  errmsg("could not write file \"%s\": %m",
820  PGSS_DUMP_FILE ".tmp")));
821  if (qbuffer)
822  free(qbuffer);
823  if (file)
824  FreeFile(file);
825  unlink(PGSS_DUMP_FILE ".tmp");
826  unlink(PGSS_TEXT_FILE);
827 }
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:1237
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1337
#define LOG
Definition: elog.h:26
signed int int32
Definition: c.h:355
static pgssSharedState * pgss
static HTAB * pgss_hash
int errcode_for_file_access(void)
Definition: elog.c:633
static char * qtext_load_file(Size *buffer_size)
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2320
unsigned int uint32
Definition: c.h:367
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:656
#define ereport(elevel,...)
Definition: elog.h:144
#define free(a)
Definition: header.h:65
size_t Size
Definition: c.h:466
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1391
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1381
int FreeFile(FILE *file)
Definition: fd.c:2519
int errmsg(const char *fmt,...)
Definition: elog.c:824
static const uint32 PGSS_FILE_HEADER
#define PGSS_TEXT_FILE
void hash_seq_term(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1467
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 518 of file pg_stat_statements.c.

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

Referenced by _PG_init().

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

1241 {
1242  pgssHashKey key;
1243  pgssEntry *entry;
1244  char *norm_query = NULL;
1245  int encoding = GetDatabaseEncoding();
1246 
1247  Assert(query != NULL);
1248 
1249  /* Safety check... */
1250  if (!pgss || !pgss_hash)
1251  return;
1252 
1253  /*
1254  * Confine our attention to the relevant part of the string, if the query
1255  * is a portion of a multi-statement source string.
1256  *
1257  * First apply starting offset, unless it's -1 (unknown).
1258  */
1259  if (query_location >= 0)
1260  {
1261  Assert(query_location <= strlen(query));
1262  query += query_location;
1263  /* Length of 0 (or -1) means "rest of string" */
1264  if (query_len <= 0)
1265  query_len = strlen(query);
1266  else
1267  Assert(query_len <= strlen(query));
1268  }
1269  else
1270  {
1271  /* If query location is unknown, distrust query_len as well */
1272  query_location = 0;
1273  query_len = strlen(query);
1274  }
1275 
1276  /*
1277  * Discard leading and trailing whitespace, too. Use scanner_isspace()
1278  * not libc's isspace(), because we want to match the lexer's behavior.
1279  */
1280  while (query_len > 0 && scanner_isspace(query[0]))
1281  query++, query_location++, query_len--;
1282  while (query_len > 0 && scanner_isspace(query[query_len - 1]))
1283  query_len--;
1284 
1285  /*
1286  * For utility statements, we just hash the query string to get an ID.
1287  */
1288  if (queryId == UINT64CONST(0))
1289  {
1290  queryId = pgss_hash_string(query, query_len);
1291 
1292  /*
1293  * If we are unlucky enough to get a hash of zero(invalid), use
1294  * queryID as 2 instead, queryID 1 is already in use for normal
1295  * statements.
1296  */
1297  if (queryId == UINT64CONST(0))
1298  queryId = UINT64CONST(2);
1299  }
1300 
1301  /* Set up key for hashtable search */
1302  key.userid = GetUserId();
1303  key.dbid = MyDatabaseId;
1304  key.queryid = queryId;
1305 
1306  /* Lookup the hash table entry with shared lock. */
1308 
1309  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
1310 
1311  /* Create new entry, if not present */
1312  if (!entry)
1313  {
1314  Size query_offset;
1315  int gc_count;
1316  bool stored;
1317  bool do_gc;
1318 
1319  /*
1320  * Create a new, normalized query string if caller asked. We don't
1321  * need to hold the lock while doing this work. (Note: in any case,
1322  * it's possible that someone else creates a duplicate hashtable entry
1323  * in the interval where we don't hold the lock below. That case is
1324  * handled by entry_alloc.)
1325  */
1326  if (jstate)
1327  {
1329  norm_query = generate_normalized_query(jstate, query,
1330  query_location,
1331  &query_len,
1332  encoding);
1334  }
1335 
1336  /* Append new query text to file with only shared lock held */
1337  stored = qtext_store(norm_query ? norm_query : query, query_len,
1338  &query_offset, &gc_count);
1339 
1340  /*
1341  * Determine whether we need to garbage collect external query texts
1342  * while the shared lock is still held. This micro-optimization
1343  * avoids taking the time to decide this while holding exclusive lock.
1344  */
1345  do_gc = need_gc_qtexts();
1346 
1347  /* Need exclusive lock to make a new hashtable entry - promote */
1350 
1351  /*
1352  * A garbage collection may have occurred while we weren't holding the
1353  * lock. In the unlikely event that this happens, the query text we
1354  * stored above will have been garbage collected, so write it again.
1355  * This should be infrequent enough that doing it while holding
1356  * exclusive lock isn't a performance problem.
1357  */
1358  if (!stored || pgss->gc_count != gc_count)
1359  stored = qtext_store(norm_query ? norm_query : query, query_len,
1360  &query_offset, NULL);
1361 
1362  /* If we failed to write to the text file, give up */
1363  if (!stored)
1364  goto done;
1365 
1366  /* OK to create a new hashtable entry */
1367  entry = entry_alloc(&key, query_offset, query_len, encoding,
1368  jstate != NULL);
1369 
1370  /* If needed, perform garbage collection while exclusive lock held */
1371  if (do_gc)
1372  gc_qtexts();
1373  }
1374 
1375  /* Increment the counts, except when jstate is not NULL */
1376  if (!jstate)
1377  {
1378  /*
1379  * Grab the spinlock while updating the counters (see comment about
1380  * locking rules at the head of the file)
1381  */
1382  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1383 
1384  Assert(kind == PGSS_PLAN || kind == PGSS_EXEC);
1385 
1386  SpinLockAcquire(&e->mutex);
1387 
1388  /* "Unstick" entry if it was previously sticky */
1389  if (IS_STICKY(e->counters))
1390  e->counters.usage = USAGE_INIT;
1391 
1392  e->counters.calls[kind] += 1;
1393  e->counters.total_time[kind] += total_time;
1394 
1395  if (e->counters.calls[kind] == 1)
1396  {
1397  e->counters.min_time[kind] = total_time;
1398  e->counters.max_time[kind] = total_time;
1399  e->counters.mean_time[kind] = total_time;
1400  }
1401  else
1402  {
1403  /*
1404  * Welford's method for accurately computing variance. See
1405  * <http://www.johndcook.com/blog/standard_deviation/>
1406  */
1407  double old_mean = e->counters.mean_time[kind];
1408 
1409  e->counters.mean_time[kind] +=
1410  (total_time - old_mean) / e->counters.calls[kind];
1411  e->counters.sum_var_time[kind] +=
1412  (total_time - old_mean) * (total_time - e->counters.mean_time[kind]);
1413 
1414  /* calculate min and max time */
1415  if (e->counters.min_time[kind] > total_time)
1416  e->counters.min_time[kind] = total_time;
1417  if (e->counters.max_time[kind] < total_time)
1418  e->counters.max_time[kind] = total_time;
1419  }
1420  e->counters.rows += rows;
1421  e->counters.shared_blks_hit += bufusage->shared_blks_hit;
1422  e->counters.shared_blks_read += bufusage->shared_blks_read;
1425  e->counters.local_blks_hit += bufusage->local_blks_hit;
1426  e->counters.local_blks_read += bufusage->local_blks_read;
1429  e->counters.temp_blks_read += bufusage->temp_blks_read;
1433  e->counters.usage += USAGE_EXEC(total_time);
1434  e->counters.wal_records += walusage->wal_records;
1435  e->counters.wal_fpi += walusage->wal_fpi;
1436  e->counters.wal_bytes += walusage->wal_bytes;
1437 
1438  SpinLockRelease(&e->mutex);
1439  }
1440 
1441 done:
1443 
1444  /* We postpone this clean-up until we're out of the lock */
1445  if (norm_query)
1446  pfree(norm_query);
1447 }
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
int64 local_blks_written
Oid GetUserId(void)
Definition: miscinit.c:448
instr_time blk_read_time
Definition: instrument.h:31
int64 shared_blks_dirtied
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:202
long shared_blks_read
Definition: instrument.h:22
double max_time[PGSS_NUMKIND]
long temp_blks_written
Definition: instrument.h:30
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:908
Counters counters
int64 temp_blks_read
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1812
long shared_blks_written
Definition: instrument.h:24
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
void pfree(void *pointer)
Definition: mcxt.c:1056
int64 shared_blks_hit
static HTAB * pgss_hash
long wal_records
Definition: instrument.h:37
#define USAGE_EXEC(duration)
static pgssEntry * entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding, bool sticky)
static char * generate_normalized_query(pgssJumbleState *jstate, const char *query, int query_loc, int *query_len_p, int encoding)
long shared_blks_dirtied
Definition: instrument.h:23
#define IS_STICKY(c)
long temp_blks_read
Definition: instrument.h:29
int64 temp_blks_written
static bool qtext_store(const char *query, int query_len, Size *query_offset, int *gc_count)
int64 shared_blks_written
#define SpinLockRelease(lock)
Definition: spin.h:64
int64 local_blks_read
static bool need_gc_qtexts(void)
int GetDatabaseEncoding(void)
Definition: mbutils.c:1151
static void gc_qtexts(void)
double mean_time[PGSS_NUMKIND]
Oid MyDatabaseId
Definition: globals.c:85
bool scanner_isspace(char ch)
Definition: scansup.c:220
#define USAGE_INIT
int64 calls[PGSS_NUMKIND]
double min_time[PGSS_NUMKIND]
double blk_read_time
#define Assert(condition)
Definition: c.h:738
int64 local_blks_dirtied
instr_time blk_write_time
Definition: instrument.h:32
int64 local_blks_hit
size_t Size
Definition: c.h:466
long wal_fpi
Definition: instrument.h:38
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1208
double blk_write_time
int32 encoding
Definition: pg_database.h:41
double sum_var_time[PGSS_NUMKIND]
e
Definition: preproc-init.c:82
long shared_blks_hit
Definition: instrument.h:21
long local_blks_written
Definition: instrument.h:28
uint64 wal_bytes
Definition: instrument.h:39

◆ qtext_fetch()

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

Definition at line 2192 of file pg_stat_statements.c.

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

2194 {
2195  /* File read failed? */
2196  if (buffer == NULL)
2197  return NULL;
2198  /* Bogus offset/length? */
2199  if (query_len < 0 ||
2200  query_offset + query_len >= buffer_size)
2201  return NULL;
2202  /* As a further sanity check, make sure there's a trailing null */
2203  if (buffer[query_offset + query_len] != '\0')
2204  return NULL;
2205  /* Looks OK */
2206  return buffer + query_offset;
2207 }

◆ qtext_load_file()

static char * qtext_load_file ( Size buffer_size)
static

Definition at line 2112 of file pg_stat_statements.c.

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

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

2113 {
2114  char *buf;
2115  int fd;
2116  struct stat stat;
2117 
2118  fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDONLY | PG_BINARY);
2119  if (fd < 0)
2120  {
2121  if (errno != ENOENT)
2122  ereport(LOG,
2124  errmsg("could not read file \"%s\": %m",
2125  PGSS_TEXT_FILE)));
2126  return NULL;
2127  }
2128 
2129  /* Get file length */
2130  if (fstat(fd, &stat))
2131  {
2132  ereport(LOG,
2134  errmsg("could not stat file \"%s\": %m",
2135  PGSS_TEXT_FILE)));
2136  CloseTransientFile(fd);
2137  return NULL;
2138  }
2139 
2140  /* Allocate buffer; beware that off_t might be wider than size_t */
2141  if (stat.st_size <= MaxAllocHugeSize)
2142  buf = (char *) malloc(stat.st_size);
2143  else
2144  buf = NULL;
2145  if (buf == NULL)
2146  {
2147  ereport(LOG,
2148  (errcode(ERRCODE_OUT_OF_MEMORY),
2149  errmsg("out of memory"),
2150  errdetail("Could not allocate enough memory to read file \"%s\".",
2151  PGSS_TEXT_FILE)));
2152  CloseTransientFile(fd);
2153  return NULL;
2154  }
2155 
2156  /*
2157  * OK, slurp in the file. If we get a short read and errno doesn't get
2158  * set, the reason is probably that garbage collection truncated the file
2159  * since we did the fstat(), so we don't log a complaint --- but we don't