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 2586 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 2588 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 1493 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 1488 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 1489 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 1490 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 1491 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 1492 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:53
static shmem_startup_hook_type prev_shmem_startup_hook
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:72
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c: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  false,
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:8826
void RequestAddinShmemSpace(Size size)
Definition: ipci.c:71
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1570
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:8911
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:8939
static ExecutorFinish_hook_type prev_ExecutorFinish
planner_hook_type planner_hook
Definition: planner.c:74
Definition: guc.h:72
static PlannedStmt * pgss_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
static Size pgss_memsize(void)
shmem_startup_hook_type shmem_startup_hook
Definition: ipci.c:53
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
static bool pgss_track_utility
static shmem_startup_hook_type prev_shmem_startup_hook
static void pgss_ExecutorFinish(QueryDesc *queryDesc)
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:72
static void pgss_ExecutorEnd(QueryDesc *queryDesc)
void DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, bool bootValue, GucContext context, int flags, GucBoolCheckHook check_hook, GucBoolAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:8800
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 2550 of file pg_stat_statements.c.

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

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

◆ comp_location()

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

Definition at line 3444 of file pg_stat_statements.c.

Referenced by fill_in_constant_lengths().

3445 {
3446  int l = ((const pgssLocationLen *) a)->location;
3447  int r = ((const pgssLocationLen *) b)->location;
3448 
3449  if (l < r)
3450  return -1;
3451  else if (l > r)
3452  return +1;
3453  else
3454  return 0;
3455 }

◆ entry_alloc()

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

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

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

◆ entry_cmp()

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

Definition at line 1935 of file pg_stat_statements.c.

Referenced by entry_dealloc().

1936 {
1937  double l_usage = (*(pgssEntry *const *) lhs)->counters.usage;
1938  double r_usage = (*(pgssEntry *const *) rhs)->counters.usage;
1939 
1940  if (l_usage < r_usage)
1941  return -1;
1942  else if (l_usage > r_usage)
1943  return +1;
1944  else
1945  return 0;
1946 }

◆ entry_dealloc()

static void entry_dealloc ( void  )
static

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

1955 {
1956  HASH_SEQ_STATUS hash_seq;
1957  pgssEntry **entries;
1958  pgssEntry *entry;
1959  int nvictims;
1960  int i;
1961  Size tottextlen;
1962  int nvalidtexts;
1963 
1964  /*
1965  * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
1966  * While we're scanning the table, apply the decay factor to the usage
1967  * values, and update the mean query length.
1968  *
1969  * Note that the mean query length is almost immediately obsolete, since
1970  * we compute it before not after discarding the least-used entries.
1971  * Hopefully, that doesn't affect the mean too much; it doesn't seem worth
1972  * making two passes to get a more current result. Likewise, the new
1973  * cur_median_usage includes the entries we're about to zap.
1974  */
1975 
1976  entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
1977 
1978  i = 0;
1979  tottextlen = 0;
1980  nvalidtexts = 0;
1981 
1982  hash_seq_init(&hash_seq, pgss_hash);
1983  while ((entry = hash_seq_search(&hash_seq)) != NULL)
1984  {
1985  entries[i++] = entry;
1986  /* "Sticky" entries get a different usage decay rate. */
1987  if (IS_STICKY(entry->counters))
1989  else
1991  /* In the mean length computation, ignore dropped texts. */
1992  if (entry->query_len >= 0)
1993  {
1994  tottextlen += entry->query_len + 1;
1995  nvalidtexts++;
1996  }
1997  }
1998 
1999  /* Sort into increasing order by usage */
2000  qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
2001 
2002  /* Record the (approximate) median usage */
2003  if (i > 0)
2004  pgss->cur_median_usage = entries[i / 2]->counters.usage;
2005  /* Record the mean query length */
2006  if (nvalidtexts > 0)
2007  pgss->mean_query_len = tottextlen / nvalidtexts;
2008  else
2010 
2011  /* Now zap an appropriate fraction of lowest-usage entries */
2012  nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
2013  nvictims = Min(nvictims, i);
2014 
2015  for (i = 0; i < nvictims; i++)
2016  {
2017  hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
2018  }
2019 
2020  pfree(entries);
2021 }
#define USAGE_DEALLOC_PERCENT
#define Min(x, y)
Definition: c.h:927
static int entry_cmp(const void *lhs, const void *rhs)
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1356
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:927
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:921
size_t Size
Definition: c.h:473
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1410
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1400
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 2454 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().

2455 {
2456  HASH_SEQ_STATUS hash_seq;
2457  pgssEntry *entry;
2458  FILE *qfile;
2459  long num_entries;
2460  long num_remove = 0;
2461  pgssHashKey key;
2462 
2463  if (!pgss || !pgss_hash)
2464  ereport(ERROR,
2465  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2466  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
2467 
2469  num_entries = hash_get_num_entries(pgss_hash);
2470 
2471  if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
2472  {
2473  /* If all the parameters are available, use the fast path. */
2474  key.userid = userid;
2475  key.dbid = dbid;
2476  key.queryid = queryid;
2477 
2478  /* Remove the key if exists */
2479  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
2480  if (entry) /* found */
2481  num_remove++;
2482  }
2483  else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
2484  {
2485  /* Remove entries corresponding to valid parameters. */
2486  hash_seq_init(&hash_seq, pgss_hash);
2487  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2488  {
2489  if ((!userid || entry->key.userid == userid) &&
2490  (!dbid || entry->key.dbid == dbid) &&
2491  (!queryid || entry->key.queryid == queryid))
2492  {
2493  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2494  num_remove++;
2495  }
2496  }
2497  }
2498  else
2499  {
2500  /* Remove all entries. */
2501  hash_seq_init(&hash_seq, pgss_hash);
2502  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2503  {
2504  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2505  num_remove++;
2506  }
2507  }
2508 
2509  /* All entries are removed? */
2510  if (num_entries != num_remove)
2511  goto release_lock;
2512 
2513  /*
2514  * Write new empty query file, perhaps even creating a new one to recover
2515  * if the file was missing.
2516  */
2518  if (qfile == NULL)
2519  {
2520  ereport(LOG,
2522  errmsg("could not create file \"%s\": %m",
2523  PGSS_TEXT_FILE)));
2524  goto done;
2525  }
2526 
2527  /* If ftruncate fails, log it, but it's not a fatal problem */
2528  if (ftruncate(fileno(qfile), 0) != 0)
2529  ereport(LOG,
2531  errmsg("could not truncate file \"%s\": %m",
2532  PGSS_TEXT_FILE)));
2533 
2534  FreeFile(qfile);
2535 
2536 done:
2537  pgss->extent = 0;
2538  /* This counts as a query text garbage collection for our purposes */
2539  record_gc_qtexts();
2540 
2541 release_lock:
2543 }
int errcode(int sqlerrcode)
Definition: elog.c:610
#define PG_BINARY_W
Definition: c.h:1243
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1356
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:927
#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:2322
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:1410
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1400
int FreeFile(FILE *file)
Definition: fd.c:2521
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 3344 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().

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

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

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

◆ JumbleExpr()

static void JumbleExpr ( pgssJumbleState jstate,
Node node 
)
static

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

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

2602 {
2603  Assert(IsA(query, Query));
2604  Assert(query->utilityStmt == NULL);
2605 
2606  APP_JUMB(query->commandType);
2607  /* resultRelation is usually predictable from commandType */
2608  JumbleExpr(jstate, (Node *) query->cteList);
2609  JumbleRangeTable(jstate, query->rtable);
2610  JumbleExpr(jstate, (Node *) query->jointree);
2611  JumbleExpr(jstate, (Node *) query->targetList);
2612  JumbleExpr(jstate, (Node *) query->onConflict);
2613  JumbleExpr(jstate, (Node *) query->returningList);
2614  JumbleExpr(jstate, (Node *) query->groupClause);
2615  JumbleExpr(jstate, (Node *) query->groupingSets);
2616  JumbleExpr(jstate, query->havingQual);
2617  JumbleExpr(jstate, (Node *) query->windowClause);
2618  JumbleExpr(jstate, (Node *) query->distinctClause);
2619  JumbleExpr(jstate, (Node *) query->sortClause);
2620  JumbleExpr(jstate, query->limitOffset);
2621  JumbleExpr(jstate, query->limitCount);
2622  JumbleRowMarks(jstate, query->rowMarks);
2623  JumbleExpr(jstate, query->setOperations);
2624 }
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:745
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 2630 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().

2631 {
2632  ListCell *lc;
2633 
2634  foreach(lc, rtable)
2635  {
2637 
2638  APP_JUMB(rte->rtekind);
2639  switch (rte->rtekind)
2640  {
2641  case RTE_RELATION:
2642  APP_JUMB(rte->relid);
2643  JumbleExpr(jstate, (Node *) rte->tablesample);
2644  break;
2645  case RTE_SUBQUERY:
2646  JumbleQuery(jstate, rte->subquery);
2647  break;
2648  case RTE_JOIN:
2649  APP_JUMB(rte->jointype);
2650  break;
2651  case RTE_FUNCTION:
2652  JumbleExpr(jstate, (Node *) rte->functions);
2653  break;
2654  case RTE_TABLEFUNC:
2655  JumbleExpr(jstate, (Node *) rte->tablefunc);
2656  break;
2657  case RTE_VALUES:
2658  JumbleExpr(jstate, (Node *) rte->values_lists);
2659  break;
2660  case RTE_CTE:
2661 
2662  /*
2663  * Depending on the CTE name here isn't ideal, but it's the
2664  * only info we have to identify the referenced WITH item.
2665  */
2666  APP_JUMB_STRING(rte->ctename);
2667  APP_JUMB(rte->ctelevelsup);
2668  break;
2669  case RTE_NAMEDTUPLESTORE:
2670  APP_JUMB_STRING(rte->enrname);
2671  break;
2672  case RTE_RESULT:
2673  break;
2674  default:
2675  elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
2676  break;
2677  }
2678  }
2679 }
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 2685 of file pg_stat_statements.c.

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

Referenced by JumbleQuery().

2686 {
2687  ListCell *lc;
2688 
2689  foreach(lc, rowMarks)
2690  {
2691  RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
2692 
2693  if (!rowmark->pushedDown)
2694  {
2695  APP_JUMB(rowmark->rti);
2696  APP_JUMB(rowmark->strength);
2697  APP_JUMB(rowmark->waitPolicy);
2698  }
2699  }
2700 }
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 2223 of file pg_stat_statements.c.

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

Referenced by gc_qtexts(), and pgss_store().

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

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

References pg_stat_statements_internal(), and PGSS_V1_0.

1541 {
1542  /* If it's really API 1.1, we'll figure that out below */
1543  pg_stat_statements_internal(fcinfo, PGSS_V1_0, true);
1544 
1545  return (Datum) 0;
1546 }
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 1526 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_2.

1527 {
1528  bool showtext = PG_GETARG_BOOL(0);
1529 
1530  pg_stat_statements_internal(fcinfo, PGSS_V1_2, showtext);
1531 
1532  return (Datum) 0;
1533 }
#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 1516 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_3.

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

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_8.

1507 {
1508  bool showtext = PG_GETARG_BOOL(0);
1509 
1510  pg_stat_statements_internal(fcinfo, PGSS_V1_8, showtext);
1511 
1512  return (Datum) 0;
1513 }
#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 1550 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().

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

References entry_reset(), and PG_RETURN_VOID.

1481 {
1482  entry_reset(0, 0, 0);
1483 
1484  PG_RETURN_VOID();
1485 }
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 1461 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.

1462 {
1463  Oid userid;
1464  Oid dbid;
1465  uint64 queryid;
1466 
1467  userid = PG_GETARG_OID(0);
1468  dbid = PG_GETARG_OID(1);
1469  queryid = (uint64) PG_GETARG_INT64(2);
1470 
1471  entry_reset(userid, dbid, queryid);
1472 
1473  PG_RETURN_VOID();
1474 }
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 1221 of file pg_stat_statements.c.

References DatumGetUInt64, and hash_any_extended().

Referenced by pgss_store().

1222 {
1223  return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
1224  len, 0));
1225 }
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 1870 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1871 {
1872  Size size;
1873 
1874  size = MAXALIGN(sizeof(pgssSharedState));
1875  size = add_size(size, hash_estimate_size(pgss_max, sizeof(pgssEntry)));
1876 
1877  return size;
1878 }
static int pgss_max
Size hash_estimate_size(long num_entries, Size entrysize)
Definition: dynahash.c:753
Size add_size(Size s1, Size s2)
Definition: shmem.c:498
size_t Size
Definition: c.h:473
#define MAXALIGN(LEN)
Definition: c.h:698

◆ 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:745
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  /*
1174  * Track the total number of rows retrieved or affected by
1175  * the utility statements of COPY, FETCH, CREATE TABLE AS,
1176  * CREATE MATERIALIZED VIEW and SELECT INTO.
1177  */
1178  rows = (qc && (qc->commandTag == CMDTAG_COPY ||
1179  qc->commandTag == CMDTAG_FETCH ||
1180  qc->commandTag == CMDTAG_SELECT)) ?
1181  qc->nprocessed : 0;
1182 
1183  /* calc differences of buffer counters. */
1184  memset(&bufusage, 0, sizeof(BufferUsage));
1185  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
1186 
1187  /* calc differences of WAL counters. */
1188  memset(&walusage, 0, sizeof(WalUsage));
1189  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
1190 
1191  pgss_store(queryString,
1192  0, /* signal that it's a utility stmt */
1193  pstmt->stmt_location,
1194  pstmt->stmt_len,
1195  PGSS_EXEC,
1196  INSTR_TIME_GET_MILLISEC(duration),
1197  rows,
1198  &bufusage,
1199  &walusage,
1200  NULL);
1201  }
1202  else
1203  {
1204  if (prev_ProcessUtility)
1205  prev_ProcessUtility(pstmt, queryString,
1206  context, params, queryEnv,
1207  dest, qc);
1208  else
1209  standard_ProcessUtility(pstmt, queryString,
1210  context, params, queryEnv,
1211  dest, qc);
1212  }
1213 }
#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:1243
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1356
#define LOG
Definition: elog.h:26
signed int int32
Definition: c.h:362
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:2322
unsigned int uint32
Definition: c.h:374
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:658
#define ereport(elevel,...)
Definition: elog.h:144
#define free(a)
Definition: header.h:65
size_t Size
Definition: c.h:473
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1410
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1400
int FreeFile(FILE *file)
Definition: fd.c:2521
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:1486
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:1243
Counters counters
#define LOG
Definition: elog.h:26
#define PG_BINARY_R
Definition: c.h:1242
signed int int32
Definition: c.h:362
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:2322
unsigned int uint32
Definition: c.h:374
#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:921
#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:473
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:2521
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 1242 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().

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

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

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

◆ qtext_load_file()

static char * qtext_load_file ( Size buffer_size)
static

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

2121 {
2122  char *buf;
2123  int fd;
2124  struct stat stat;
2125 
2126  fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDONLY | PG_BINARY);
2127  if (fd < 0)
2128  {
2129  if (errno != ENOENT)
2130  ereport(LOG,
2132  errmsg("could not read file \"%s\": %m",
2133  PGSS_TEXT_FILE)));
2134  return NULL;
2135  }
2136 
2137  /* Get file length */
2138  if (fstat(fd, &stat))
2139  {
2140  ereport(LOG,
2142  errmsg("could not stat file \"%s\": %m",
2143  PGSS_TEXT_FILE)));
2144  CloseTransientFile(fd);
2145  return NULL;
2146  }
2147 
2148  /* Allocate buffer; beware that off_t might be wider than size_t */
2149  if (stat.st_size <= MaxAllocHugeSize)
2150  buf = (char *) malloc(stat.st_size);
2151  else
2152  buf = NULL;
2153  if (buf == NULL)
2154  {
2155  ereport(LOG,
2156  (errcode(ERRCODE_OUT_OF_MEMORY),
2157  errmsg("out of memory"),
2158  errdetail("Could not allocate enough memory to read file \"%s\".",
2159