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)
 
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 2585 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 2587 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 1492 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 1487 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 1488 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 1489 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 1490 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 1491 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:8854
void RequestAddinShmemSpace(Size size)
Definition: ipci.c:71
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1596
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:8939
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:8967
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:8828
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 2549 of file pg_stat_statements.c.

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

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

◆ comp_location()

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

Definition at line 3443 of file pg_stat_statements.c.

Referenced by fill_in_constant_lengths().

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

◆ entry_alloc()

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

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

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

◆ entry_cmp()

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

Definition at line 1934 of file pg_stat_statements.c.

Referenced by entry_dealloc().

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

◆ entry_dealloc()

static void entry_dealloc ( void  )
static

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

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

◆ entry_reset()

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

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

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

◆ fill_in_constant_lengths()

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

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

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

◆ gc_qtexts()

static void gc_qtexts ( void  )
static

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

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

◆ generate_normalized_query()

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

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

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

◆ JumbleExpr()

static void JumbleExpr ( pgssJumbleState jstate,
Node node 
)
static

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

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

◆ JumbleQuery()

static void JumbleQuery ( pgssJumbleState jstate,
Query query 
)
static

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

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

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

◆ JumbleRowMarks()

static void JumbleRowMarks ( pgssJumbleState jstate,
List rowMarks 
)
static

Definition at line 2684 of file pg_stat_statements.c.

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

Referenced by JumbleQuery().

2685 {
2686  ListCell *lc;
2687 
2688  foreach(lc, rowMarks)
2689  {
2690  RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
2691 
2692  if (!rowmark->pushedDown)
2693  {
2694  APP_JUMB(rowmark->rti);
2695  APP_JUMB(rowmark->strength);
2696  APP_JUMB(rowmark->waitPolicy);
2697  }
2698  }
2699 }
LockClauseStrength strength
Definition: parsenodes.h:1384
#define lfirst_node(type, lc)
Definition: pg_list.h:172
#define APP_JUMB(item)
LockWaitPolicy waitPolicy
Definition: parsenodes.h:1385

◆ need_gc_qtexts()

static bool need_gc_qtexts ( void  )
static

Definition at line 2222 of file pg_stat_statements.c.

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

Referenced by gc_qtexts(), and pgss_store().

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

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

References pg_stat_statements_internal(), and PGSS_V1_0.

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

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_2.

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

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_3.

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

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_8.

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

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

References entry_reset(), and PG_RETURN_VOID.

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

1461 {
1462  Oid userid;
1463  Oid dbid;
1464  uint64 queryid;
1465 
1466  userid = PG_GETARG_OID(0);
1467  dbid = PG_GETARG_OID(1);
1468  queryid = (uint64) PG_GETARG_INT64(2);
1469 
1470  entry_reset(userid, dbid, queryid);
1471 
1472  PG_RETURN_VOID();
1473 }
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:90
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:121
int stmt_location
Definition: plannodes.h:89
BufferUsage bufusage
Definition: instrument.h:74
struct Instrumentation * totaltime
Definition: execdesc.h:55
uint64 es_processed
Definition: execnodes.h:563
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:559
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 1869 of file pg_stat_statements.c.

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

Referenced by _PG_init().

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

◆ 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:147
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition: instrument.c:225
static planner_hook_type prev_planner_hook
static bool pgss_track_planning
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
uint64 queryId
Definition: parsenodes.h:116
#define PG_FINALLY()
Definition: elog.h: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:746
void * palloc(Size size)
Definition: mcxt.c:950
int stmt_len
Definition: parsenodes.h:182

◆ pgss_ProcessUtility()

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

Definition at line 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:579
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:528
static void pgss_store(const char *query, uint64 queryId, int query_location, int query_len, pgssStoreKind kind, double total_time, uint64 rows, const BufferUsage *bufusage, const WalUsage *walusage, pgssJumbleState *jstate)
static int exec_nested_level
int stmt_len
Definition: plannodes.h:90
int duration
Definition: pgbench.c:147
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition: instrument.c:225
uint64 nprocessed
Definition: cmdtag.h:31
void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:541
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
int stmt_location
Definition: plannodes.h:89
Node * utilityStmt
Definition: plannodes.h:86
#define PG_FINALLY()
Definition: elog.h: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:1216
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1347
#define LOG
Definition: elog.h:26
signed int int32
Definition: c.h:363
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:375
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:659
#define ereport(elevel,...)
Definition: elog.h:144
#define free(a)
Definition: header.h:65
size_t Size
Definition: c.h:474
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1401
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1391
int FreeFile(FILE *file)
Definition: fd.c:2521
int errmsg(const char *fmt,...)
Definition: elog.c:821
static const uint32 PGSS_FILE_HEADER
#define PGSS_TEXT_FILE
void hash_seq_term(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1477
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:85
static const uint32 PGSS_PG_MAJOR_VERSION
#define SpinLockInit(lock)
Definition: spin.h:60
Size entrysize
Definition: hsearch.h:72
#define PGSS_DUMP_FILE
int errcode(int sqlerrcode)
Definition: elog.c:610
struct pgssEntry pgssEntry
#define PG_BINARY_W
Definition: c.h:1216
Counters counters
#define LOG
Definition: elog.h:26
#define PG_BINARY_R
Definition: c.h:1215
signed int int32
Definition: c.h:363
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1812
#define ASSUMED_LENGTH_INIT
static pgssSharedState * pgss
void pfree(void *pointer)
Definition: mcxt.c:1057
static int pgss_max
static HTAB * pgss_hash
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c: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:375
#define IS_STICKY(c)
#define HASH_BLOBS
Definition: hsearch.h:86
uintptr_t Datum
Definition: postgres.h:367
static void pgss_shmem_shutdown(int code, Datum arg)
Size keysize
Definition: hsearch.h:71
pgssHashKey key
#define ereport(elevel,...)
Definition: elog.h:144
#define Max(x, y)
Definition: c.h:922
#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:474
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1208
static void header(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:210
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1070
static shmem_startup_hook_type prev_shmem_startup_hook
int FreeFile(FILE *file)
Definition: fd.c:2521
void * palloc(Size size)
Definition: mcxt.c:950
int errmsg(const char *fmt,...)
Definition: elog.c:821
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);
1341  }
1342 
1343  /* Append new query text to file with only shared lock held */
1344  stored = qtext_store(norm_query ? norm_query : query, query_len,
1345  &query_offset, &gc_count);
1346 
1347  /*
1348  * Determine whether we need to garbage collect external query texts
1349  * while the shared lock is still held. This micro-optimization
1350  * avoids taking the time to decide this while holding exclusive lock.
1351  */
1352  do_gc = need_gc_qtexts();
1353 
1354  /* Need exclusive lock to make a new hashtable entry - promote */
1357 
1358  /*
1359  * A garbage collection may have occurred while we weren't holding the
1360  * lock. In the unlikely event that this happens, the query text we
1361  * stored above will have been garbage collected, so write it again.
1362  * This should be infrequent enough that doing it while holding
1363  * exclusive lock isn't a performance problem.
1364  */
1365  if (!stored || pgss->gc_count != gc_count)
1366  stored = qtext_store(norm_query ? norm_query : query, query_len,
1367  &query_offset, NULL);
1368 
1369  /* If we failed to write to the text file, give up */
1370  if (!stored)
1371  goto done;
1372 
1373  /* OK to create a new hashtable entry */
1374  entry = entry_alloc(&key, query_offset, query_len, encoding,
1375  jstate != NULL);
1376 
1377  /* If needed, perform garbage collection while exclusive lock held */
1378  if (do_gc)
1379  gc_qtexts();
1380  }
1381 
1382  /* Increment the counts, except when jstate is not NULL */
1383  if (!jstate)
1384  {
1385  /*
1386  * Grab the spinlock while updating the counters (see comment about
1387  * locking rules at the head of the file)
1388  */
1389  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1390 
1391  Assert(kind == PGSS_PLAN || kind == PGSS_EXEC);
1392 
1393  SpinLockAcquire(&e->mutex);
1394 
1395  /* "Unstick" entry if it was previously sticky */
1396  if (IS_STICKY(e->counters))
1397  e->counters.usage = USAGE_INIT;
1398 
1399  e->counters.calls[kind] += 1;
1400  e->counters.total_time[kind] += total_time;
1401 
1402  if (e->counters.calls[kind] == 1)
1403  {
1404  e->counters.min_time[kind] = total_time;
1405  e->counters.max_time[kind] = total_time;
1406  e->counters.mean_time[kind] = total_time;
1407  }
1408  else
1409  {
1410  /*
1411  * Welford's method for accurately computing variance. See
1412  * <http://www.johndcook.com/blog/standard_deviation/>
1413  */
1414  double old_mean = e->counters.mean_time[kind];
1415 
1416  e->counters.mean_time[kind] +=
1417  (total_time - old_mean) / e->counters.calls[kind];
1418  e->counters.sum_var_time[kind] +=
1419  (total_time - old_mean) * (total_time - e->counters.mean_time[kind]);
1420 
1421  /* calculate min and max time */
1422  if (e->counters.min_time[kind] > total_time)
1423  e->counters.min_time[kind] = total_time;
1424  if (e->counters.max_time[kind] < total_time)
1425  e->counters.max_time[kind] = total_time;
1426  }
1427  e->counters.rows += rows;
1428  e->counters.shared_blks_hit += bufusage->shared_blks_hit;
1429  e->counters.shared_blks_read += bufusage->shared_blks_read;
1432  e->counters.local_blks_hit += bufusage->local_blks_hit;
1433  e->counters.local_blks_read += bufusage->local_blks_read;
1436  e->counters.temp_blks_read += bufusage->temp_blks_read;
1440  e->counters.usage += USAGE_EXEC(total_time);
1441  e->counters.wal_records += walusage->wal_records;
1442  e->counters.wal_fpi += walusage->wal_fpi;
1443  e->counters.wal_bytes += walusage->wal_bytes;
1444 
1445  SpinLockRelease(&e->mutex);
1446  }
1447 
1448 done:
1450 
1451  /* We postpone this clean-up until we're out of the lock */
1452  if (norm_query)
1453  pfree(norm_query);
1454 }
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:476
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:919
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:1057
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)
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:117
#define USAGE_INIT
int64 calls[PGSS_NUMKIND]
double min_time[PGSS_NUMKIND]
double blk_read_time
#define Assert(condition)
Definition: c.h:746
int64 local_blks_dirtied
instr_time blk_write_time
Definition: instrument.h:32
int64 local_blks_hit
size_t Size
Definition: c.h:474
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
static char * generate_normalized_query(pgssJumbleState *jstate, const char *query, int query_loc, int *query_len_p)

◆ qtext_fetch()

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

Definition at line 2199 of file pg_stat_statements.c.

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

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

◆ qtext_load_file()

static char * qtext_load_file ( Size buffer_size)
static

Definition at line 2119 of file pg_stat_statements.c.

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

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

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