PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_stash_advice.c File Reference
#include "postgres.h"
#include "common/hashfn.h"
#include "common/string.h"
#include "miscadmin.h"
#include "nodes/queryjumble.h"
#include "pg_plan_advice.h"
#include "pg_stash_advice.h"
#include "postmaster/bgworker.h"
#include "storage/dsm_registry.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "lib/simplehash.h"
Include dependency graph for pg_stash_advice.c:

Go to the source code of this file.

Macros

#define SH_PREFIX   pgsa_stash_name_table
 
#define SH_ELEMENT_TYPE   pgsa_stash_name
 
#define SH_KEY_TYPE   uint64
 
#define SH_KEY   pgsa_stash_id
 
#define SH_HASH_KEY(tb, key)   hash_bytes((const unsigned char *) &(key), sizeof(uint64))
 
#define SH_EQUAL(tb, a, b)   (a == b)
 
#define SH_SCOPE   extern
 
#define SH_DEFINE
 

Functions

 PG_MODULE_MAGIC_EXT (.name="pg_stash_advice",.version=PG_VERSION)
 
static charpgsa_advisor (PlannerGlobal *glob, Query *parse, const char *query_string, int cursorOptions, ExplainState *es)
 
static bool pgsa_check_stash_name_guc (char **newval, void **extra, GucSource source)
 
static void pgsa_init_shared_state (void *ptr, void *arg)
 
static bool pgsa_is_identifier (char *str)
 
void _PG_init (void)
 
void pgsa_attach (void)
 
void pgsa_check_lockout (void)
 
void pgsa_check_stash_name (char *stash_name)
 
void pgsa_create_stash (char *stash_name)
 
void pgsa_clear_advice_string (char *stash_name, int64 queryId)
 
void pgsa_drop_stash (char *stash_name)
 
void pgsa_reset_all_stashes (void)
 
uint64 pgsa_lookup_stash_id (char *stash_name)
 
void pgsa_set_advice_string (char *stash_name, int64 queryId, char *advice_string)
 
void pgsa_start_worker (void)
 

Variables

static dshash_parameters pgsa_stash_dshash_parameters
 
static dshash_parameters pgsa_entry_dshash_parameters
 
static charpg_stash_advice_stash_name = ""
 
bool pg_stash_advice_persist = true
 
int pg_stash_advice_persist_interval = 30
 
pgsa_shared_statepgsa_state
 
dsa_areapgsa_dsa_area
 
dshash_tablepgsa_stash_dshash
 
dshash_tablepgsa_entry_dshash
 
static MemoryContext pg_stash_advice_mcxt
 

Macro Definition Documentation

◆ SH_DEFINE

#define SH_DEFINE

Definition at line 82 of file pg_stash_advice.c.

◆ SH_ELEMENT_TYPE

#define SH_ELEMENT_TYPE   pgsa_stash_name

Definition at line 76 of file pg_stash_advice.c.

◆ SH_EQUAL

#define SH_EQUAL (   tb,
  a,
  b 
)    (a == b)

Definition at line 80 of file pg_stash_advice.c.

◆ SH_HASH_KEY

#define SH_HASH_KEY (   tb,
  key 
)    hash_bytes((const unsigned char *) &(key), sizeof(uint64))

Definition at line 79 of file pg_stash_advice.c.

◆ SH_KEY

#define SH_KEY   pgsa_stash_id

Definition at line 78 of file pg_stash_advice.c.

◆ SH_KEY_TYPE

#define SH_KEY_TYPE   uint64

Definition at line 77 of file pg_stash_advice.c.

◆ SH_PREFIX

#define SH_PREFIX   pgsa_stash_name_table

Definition at line 75 of file pg_stash_advice.c.

◆ SH_SCOPE

#define SH_SCOPE   extern

Definition at line 81 of file pg_stash_advice.c.

Function Documentation

◆ _PG_init()

void _PG_init ( void  )

Definition at line 89 of file pg_stash_advice.c.

90{
92
93 /* If compute_query_id = 'auto', we would like query IDs. */
95
96 /* Define our GUCs. */
98 DefineCustomBoolVariable("pg_stash_advice.persist",
99 "Save and restore advice stash contents across restarts.",
100 NULL,
102 true,
104 0,
105 NULL,
106 NULL,
107 NULL);
108 else
110
111 DefineCustomIntVariable("pg_stash_advice.persist_interval",
112 "Interval between advice stash saves, in seconds.",
113 NULL,
115 30,
116 0,
117 3600,
120 NULL,
121 NULL,
122 NULL);
123
124 DefineCustomStringVariable("pg_stash_advice.stash_name",
125 "Name of the advice stash to be used in this session.",
126 NULL,
128 "",
130 0,
132 NULL,
133 NULL);
134
135 MarkGUCPrefixReserved("pg_stash_advice");
136
137 /* Start the background worker for persistence, if enabled. */
140
141 /* Tell pg_plan_advice that we want to provide advice strings. */
143 load_external_function("pg_plan_advice", "pg_plan_advice_add_advisor",
144 true, NULL);
145 (*add_advisor_fn) (pgsa_advisor);
146}
void * load_external_function(const char *filename, const char *funcname, bool signalNotFound, void **filehandle)
Definition dfmgr.c:95
void DefineCustomStringVariable(const char *name, const char *short_desc, const char *long_desc, char **valueAddr, const char *bootValue, GucContext context, int flags, GucStringCheckHook check_hook, GucStringAssignHook assign_hook, GucShowHook show_hook)
Definition guc.c:5129
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:5049
void MarkGUCPrefixReserved(const char *className)
Definition guc.c:5186
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:5073
@ PGC_USERSET
Definition guc.h:79
@ PGC_POSTMASTER
Definition guc.h:74
@ PGC_SIGHUP
Definition guc.h:75
#define GUC_UNIT_S
Definition guc.h:240
bool process_shared_preload_libraries_in_progress
Definition miscinit.c:1790
char *(* pg_plan_advice_advisor_hook)(PlannerGlobal *glob, Query *parse, const char *query_string, int cursorOptions, ExplainState *es)
bool pg_stash_advice_persist
static char * pgsa_advisor(PlannerGlobal *glob, Query *parse, const char *query_string, int cursorOptions, ExplainState *es)
void pgsa_start_worker(void)
static char * pg_stash_advice_stash_name
static bool pgsa_check_stash_name_guc(char **newval, void **extra, GucSource source)
int pg_stash_advice_persist_interval
static int fb(int x)
void EnableQueryId(void)

References DefineCustomBoolVariable(), DefineCustomIntVariable(), DefineCustomStringVariable(), EnableQueryId(), fb(), GUC_UNIT_S, load_external_function(), MarkGUCPrefixReserved(), pg_stash_advice_persist, pg_stash_advice_persist_interval, pg_stash_advice_stash_name, PGC_POSTMASTER, PGC_SIGHUP, PGC_USERSET, pgsa_advisor(), pgsa_check_stash_name_guc(), pgsa_start_worker(), and process_shared_preload_libraries_in_progress.

◆ PG_MODULE_MAGIC_EXT()

PG_MODULE_MAGIC_EXT ( name = "pg_stash_advice",
version = PG_VERSION 
)

◆ pgsa_advisor()

static char * pgsa_advisor ( PlannerGlobal glob,
Query parse,
const char query_string,
int  cursorOptions,
ExplainState es 
)
static

Definition at line 153 of file pg_stash_advice.c.

156{
158 pgsa_entry *entry;
159 char *advice_string;
161
162 /*
163 * Exit quickly if the stash name is empty or there's no query ID.
164 */
165 if (pg_stash_advice_stash_name[0] == '\0' || parse->queryId == 0)
166 return NULL;
167
168 /* Attach to dynamic shared memory if not already done. */
170 pgsa_attach();
171
172 /* If stash data is still being restored from disk, ignore. */
174 return NULL;
175
176 /*
177 * Translate pg_stash_advice.stash_name to an integer ID.
178 *
179 * pgsa_check_stash_name_guc() has already validated the advice stash
180 * name, so we don't need to call pgsa_check_stash_name() here.
181 */
183 if (stash_id == 0)
184 return NULL;
185
186 /*
187 * Look up the advice string for the given stash ID + query ID.
188 *
189 * If we find an advice string, we copy it into the current memory
190 * context, presumably short-lived, so that we can release the lock on the
191 * dshash entry. pg_plan_advice only needs the value to remain allocated
192 * long enough for it to be parsed, so this should be good enough.
193 */
194 memset(&key, 0, sizeof(pgsa_entry_key));
195 key.pgsa_stash_id = stash_id;
196 key.queryId = parse->queryId;
197 entry = dshash_find(pgsa_entry_dshash, &key, false);
198 if (entry == NULL)
199 return NULL;
200 if (entry->advice_string == InvalidDsaPointer)
201 advice_string = NULL;
202 else
203 advice_string = pstrdup(dsa_get_address(pgsa_dsa_area,
204 entry->advice_string));
206
207 /* If we found an advice string, emit a debug message. */
208 if (advice_string != NULL)
209 elog(DEBUG2, "supplying automatic advice for stash \"%s\", query ID %" PRId64 ": %s",
210 pg_stash_advice_stash_name, key.queryId, advice_string);
211
212 return advice_string;
213}
static bool pg_atomic_unlocked_test_flag(volatile pg_atomic_flag *ptr)
Definition atomics.h:189
uint64_t uint64
Definition c.h:684
#define unlikely(x)
Definition c.h:497
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition dsa.c:954
#define InvalidDsaPointer
Definition dsa.h:78
void dshash_release_lock(dshash_table *hash_table, void *entry)
Definition dshash.c:579
void * dshash_find(dshash_table *hash_table, const void *key, bool exclusive)
Definition dshash.c:394
#define DEBUG2
Definition elog.h:30
#define elog(elevel,...)
Definition elog.h:228
void parse(int)
Definition parse.c:49
char * pstrdup(const char *in)
Definition mcxt.c:1910
dshash_table * pgsa_entry_dshash
dsa_area * pgsa_dsa_area
uint64 pgsa_lookup_stash_id(char *stash_name)
void pgsa_attach(void)
pgsa_shared_state * pgsa_state
dsa_pointer advice_string
pg_atomic_flag stashes_ready

References pgsa_entry::advice_string, DEBUG2, dsa_get_address(), dshash_find(), dshash_release_lock(), elog, fb(), InvalidDsaPointer, parse(), pg_atomic_unlocked_test_flag(), pg_stash_advice_stash_name, pgsa_attach(), pgsa_dsa_area, pgsa_entry_dshash, pgsa_lookup_stash_id(), pgsa_state, pstrdup(), pgsa_shared_state::stashes_ready, and unlikely.

Referenced by _PG_init().

◆ pgsa_attach()

void pgsa_attach ( void  )

Definition at line 224 of file pg_stash_advice.c.

225{
226 bool found;
227 MemoryContext oldcontext;
228
229 /*
230 * Create a memory context to make sure that any control structures
231 * allocated in local memory are sufficiently persistent.
232 */
235 "pg_stash_advice",
238
239 /* Attach to the fixed-size state object if not already done. */
240 if (pgsa_state == NULL)
241 pgsa_state = GetNamedDSMSegment("pg_stash_advice",
242 sizeof(pgsa_shared_state),
244 &found, NULL);
245
246 /* Attach to the DSA area if not already done. */
247 if (pgsa_dsa_area == NULL)
248 {
250
254 {
259 }
260 else
261 {
264 }
266 }
267
268 /* Attach to the stash_name->stash_id hash table if not already done. */
269 if (pgsa_stash_dshash == NULL)
270 {
272
277 {
280 NULL);
284 }
285 else
286 {
291 }
292 }
293
294 /* Attach to the entry hash table if not already done. */
295 if (pgsa_entry_dshash == NULL)
296 {
298
303 {
306 NULL);
310 }
311 else
312 {
317 }
318 }
319
320 /* Restore previous memory context. */
321 MemoryContextSwitchTo(oldcontext);
322}
dsa_area * dsa_attach(dsa_handle handle)
Definition dsa.c:510
void dsa_pin_mapping(dsa_area *area)
Definition dsa.c:649
dsa_handle dsa_get_handle(dsa_area *area)
Definition dsa.c:498
void dsa_pin(dsa_area *area)
Definition dsa.c:987
#define dsa_create(tranche_id)
Definition dsa.h:117
dsm_handle dsa_handle
Definition dsa.h:136
#define DSA_HANDLE_INVALID
Definition dsa.h:139
dshash_table_handle dshash_get_hash_table_handle(dshash_table *hash_table)
Definition dshash.c:371
dshash_table * dshash_attach(dsa_area *area, const dshash_parameters *params, dshash_table_handle handle, void *arg)
Definition dshash.c:274
dshash_table * dshash_create(dsa_area *area, const dshash_parameters *params, void *arg)
Definition dshash.c:210
#define DSHASH_HANDLE_INVALID
Definition dshash.h:27
dsa_pointer dshash_table_handle
Definition dshash.h:24
void * GetNamedDSMSegment(const char *name, size_t size, void(*init_callback)(void *ptr, void *arg), bool *found, void *arg)
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1150
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1767
@ LW_EXCLUSIVE
Definition lwlock.h:104
MemoryContext TopMemoryContext
Definition mcxt.c:167
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
dshash_table * pgsa_stash_dshash
static MemoryContext pg_stash_advice_mcxt
static dshash_parameters pgsa_stash_dshash_parameters
static dshash_parameters pgsa_entry_dshash_parameters
static void pgsa_init_shared_state(void *ptr, void *arg)
dshash_table_handle entry_hash
dshash_table_handle stash_hash

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, pgsa_shared_state::area, dsa_attach(), dsa_create, dsa_get_handle(), DSA_HANDLE_INVALID, dsa_pin(), dsa_pin_mapping(), pgsa_shared_state::dsa_tranche, dshash_attach(), dshash_create(), dshash_get_hash_table_handle(), DSHASH_HANDLE_INVALID, pgsa_shared_state::entry_hash, pgsa_shared_state::entry_tranche, fb(), GetNamedDSMSegment(), pgsa_shared_state::lock, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MemoryContextSwitchTo(), pg_stash_advice_mcxt, pgsa_dsa_area, pgsa_entry_dshash, pgsa_entry_dshash_parameters, pgsa_init_shared_state(), pgsa_stash_dshash, pgsa_stash_dshash_parameters, pgsa_state, pgsa_shared_state::stash_hash, pgsa_shared_state::stash_tranche, TopMemoryContext, and dshash_parameters::tranche_id.

Referenced by pg_create_advice_stash(), pg_drop_advice_stash(), pg_get_advice_stash_contents(), pg_get_advice_stashes(), pg_set_stashed_advice(), pg_start_stash_advice_worker(), pg_stash_advice_worker_main(), and pgsa_advisor().

◆ pgsa_check_lockout()

void pgsa_check_lockout ( void  )

Definition at line 328 of file pg_stash_advice.c.

329{
333 errmsg("stash modifications are not allowed because \"%s\" has not been loaded yet",
335}
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define ereport(elevel,...)
Definition elog.h:152
static char * errmsg
#define PGSA_DUMP_FILE

References ereport, errcode(), errmsg, ERROR, fb(), pg_atomic_unlocked_test_flag(), PGSA_DUMP_FILE, pgsa_state, and pgsa_shared_state::stashes_ready.

Referenced by pg_create_advice_stash(), pg_drop_advice_stash(), and pg_set_stashed_advice().

◆ pgsa_check_stash_name()

void pgsa_check_stash_name ( char stash_name)

Definition at line 343 of file pg_stash_advice.c.

344{
345 /* Reject empty advice stash name. */
346 if (stash_name[0] == '\0')
349 errmsg("advice stash name may not be zero length"));
350
351 /* Reject overlong advice stash names. */
352 if (strlen(stash_name) + 1 > NAMEDATALEN)
355 errmsg("advice stash names may not be longer than %d bytes",
356 NAMEDATALEN - 1));
357
358 /*
359 * Reject non-ASCII advice stash names, since advice stashes are visible
360 * across all databases and the encodings of those databases might differ.
361 */
362 if (!pg_is_ascii(stash_name))
365 errmsg("advice stash name must not contain non-ASCII characters"));
366
367 /*
368 * Reject things that do not look like identifiers, since the ability to
369 * create an advice stash with non-printable characters or weird symbols
370 * in the name is not likely to be useful to anyone.
371 */
372 if (!pgsa_is_identifier(stash_name))
375 errmsg("advice stash name must begin with a letter or underscore and contain only letters, digits, and underscores"));
376}
#define NAMEDATALEN
static bool pgsa_is_identifier(char *str)
bool pg_is_ascii(const char *str)
Definition string.c:132

References ereport, errcode(), errmsg, ERROR, fb(), NAMEDATALEN, pg_is_ascii(), and pgsa_is_identifier().

Referenced by pg_create_advice_stash(), pg_drop_advice_stash(), pg_get_advice_stash_contents(), and pg_set_stashed_advice().

◆ pgsa_check_stash_name_guc()

static bool pgsa_check_stash_name_guc ( char **  newval,
void **  extra,
GucSource  source 
)
static

Definition at line 383 of file pg_stash_advice.c.

384{
385 char *stash_name = *newval;
386
387 /* Reject overlong advice stash names. */
388 if (strlen(stash_name) + 1 > NAMEDATALEN)
389 {
391 GUC_check_errdetail("advice stash names may not be longer than %d bytes",
392 NAMEDATALEN - 1);
393 return false;
394 }
395
396 /*
397 * Reject non-ASCII advice stash names, since advice stashes are visible
398 * across all databases and the encodings of those databases might differ.
399 */
400 if (!pg_is_ascii(stash_name))
401 {
403 GUC_check_errdetail("advice stash name must not contain non-ASCII characters");
404 return false;
405 }
406
407 /*
408 * Reject things that do not look like identifiers, since the ability to
409 * create an advice stash with non-printable characters or weird symbols
410 * in the name is not likely to be useful to anyone.
411 */
412 if (!pgsa_is_identifier(stash_name))
413 {
415 GUC_check_errdetail("advice stash name must begin with a letter or underscore and contain only letters, digits, and underscores");
416 return false;
417 }
418
419 return true;
420}
void GUC_check_errcode(int sqlerrcode)
Definition guc.c:6666
#define newval
#define GUC_check_errdetail
Definition guc.h:508

References fb(), GUC_check_errcode(), GUC_check_errdetail, NAMEDATALEN, newval, pg_is_ascii(), and pgsa_is_identifier().

Referenced by _PG_init().

◆ pgsa_clear_advice_string()

void pgsa_clear_advice_string ( char stash_name,
int64  queryId 
)

Definition at line 450 of file pg_stash_advice.c.

451{
452 pgsa_entry *entry;
456
458
459 /* Translate the stash name to an integer ID. */
460 if ((stash_id = pgsa_lookup_stash_id(stash_name)) == 0)
463 errmsg("advice stash \"%s\" does not exist", stash_name));
464
465 /*
466 * Look for an existing entry, and free it. But, be sure to save the
467 * pointer to the associated advice string, if any.
468 */
469 memset(&key, 0, sizeof(pgsa_entry_key));
470 key.pgsa_stash_id = stash_id;
471 key.queryId = queryId;
472 entry = dshash_find(pgsa_entry_dshash, &key, true);
473 if (entry == NULL)
475 else
476 {
477 old_dp = entry->advice_string;
479 }
480
481 /* Now we free the advice string as well, if there was one. */
484
485 /* Bump change count. */
487}
static uint64 pg_atomic_add_fetch_u64(volatile pg_atomic_uint64 *ptr, int64 add_)
Definition atomics.h:564
#define Assert(condition)
Definition c.h:1002
void dsa_free(dsa_area *area, dsa_pointer dp)
Definition dsa.c:838
uint64 dsa_pointer
Definition dsa.h:62
void dshash_delete_entry(dshash_table *hash_table, void *entry)
Definition dshash.c:562
bool LWLockHeldByMe(LWLock *lock)
Definition lwlock.c:1885
pg_atomic_uint64 change_count

References pgsa_entry::advice_string, Assert, pgsa_shared_state::change_count, dsa_free(), dshash_delete_entry(), dshash_find(), ereport, errcode(), errmsg, ERROR, fb(), InvalidDsaPointer, pgsa_shared_state::lock, LWLockHeldByMe(), pg_atomic_add_fetch_u64(), pgsa_dsa_area, pgsa_entry_dshash, pgsa_lookup_stash_id(), and pgsa_state.

Referenced by pg_set_stashed_advice().

◆ pgsa_create_stash()

void pgsa_create_stash ( char stash_name)

Definition at line 426 of file pg_stash_advice.c.

427{
429 bool found;
430
432
433 /* Create a stash with this name, unless one already exists. */
434 stash = dshash_find_or_insert(pgsa_stash_dshash, stash_name, &found);
435 if (found)
438 errmsg("advice stash \"%s\" already exists", stash_name));
439 stash->pgsa_stash_id = pgsa_state->next_stash_id++;
441
442 /* Bump change count. */
444}
#define dshash_find_or_insert(hash_table, key, found)
Definition dshash.h:109
bool LWLockHeldByMeInMode(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1929

References Assert, pgsa_shared_state::change_count, dshash_find_or_insert, dshash_release_lock(), ereport, errcode(), errmsg, ERROR, fb(), pgsa_shared_state::lock, LW_EXCLUSIVE, LWLockHeldByMeInMode(), pgsa_shared_state::next_stash_id, pg_atomic_add_fetch_u64(), pgsa_stash_dshash, and pgsa_state.

Referenced by pg_create_advice_stash(), and pgsa_restore_stashes().

◆ pgsa_drop_stash()

void pgsa_drop_stash ( char stash_name)

Definition at line 493 of file pg_stash_advice.c.

494{
495 pgsa_entry *entry;
499
501
502 /* Remove the entry for this advice stash. */
503 stash = dshash_find(pgsa_stash_dshash, stash_name, true);
504 if (stash == NULL)
507 errmsg("advice stash \"%s\" does not exist", stash_name));
508 stash_id = stash->pgsa_stash_id;
510
511 /*
512 * Now remove all the entries. Since pgsa_state->lock must be held at
513 * least in shared mode to insert entries into pgsa_entry_dshash, it
514 * doesn't matter whether we do this before or after deleting the entry
515 * from pgsa_stash_dshash.
516 */
518 while ((entry = dshash_seq_next(&iterator)) != NULL)
519 {
520 if (stash_id == entry->key.pgsa_stash_id)
521 {
522 if (entry->advice_string != InvalidDsaPointer)
525 }
526 }
528
529 /* Bump change count. */
531}
void dshash_seq_init(dshash_seq_status *status, dshash_table *hash_table, bool exclusive)
Definition dshash.c:659
void dshash_seq_term(dshash_seq_status *status)
Definition dshash.c:768
void * dshash_seq_next(dshash_seq_status *status)
Definition dshash.c:678
void dshash_delete_current(dshash_seq_status *status)
Definition dshash.c:778
uint64 pgsa_stash_id
pgsa_entry_key key

References pgsa_entry::advice_string, Assert, pgsa_shared_state::change_count, dsa_free(), dshash_delete_current(), dshash_delete_entry(), dshash_find(), dshash_seq_init(), dshash_seq_next(), dshash_seq_term(), ereport, errcode(), errmsg, ERROR, fb(), InvalidDsaPointer, pgsa_entry::key, pgsa_shared_state::lock, LW_EXCLUSIVE, LWLockHeldByMeInMode(), pg_atomic_add_fetch_u64(), pgsa_dsa_area, pgsa_entry_dshash, pgsa_stash_dshash, pgsa_entry_key::pgsa_stash_id, and pgsa_state.

Referenced by pg_drop_advice_stash().

◆ pgsa_init_shared_state()

static void pgsa_init_shared_state ( void ptr,
void arg 
)
static

Definition at line 571 of file pg_stash_advice.c.

572{
574
575 LWLockInitialize(&state->lock,
576 LWLockNewTrancheId("pg_stash_advice_lock"));
577 state->dsa_tranche = LWLockNewTrancheId("pg_stash_advice_dsa");
578 state->stash_tranche = LWLockNewTrancheId("pg_stash_advice_stash");
579 state->entry_tranche = LWLockNewTrancheId("pg_stash_advice_entry");
580 state->next_stash_id = UINT64CONST(1);
582 state->stash_hash = DSHASH_HANDLE_INVALID;
583 state->entry_hash = DSHASH_HANDLE_INVALID;
584 state->bgworker_pid = InvalidPid;
585 pg_atomic_init_flag(&state->stashes_ready);
586 pg_atomic_init_u64(&state->change_count, 0);
587
588 /*
589 * If this module was loaded via shared_preload_libraries, then
590 * pg_stash_advice_persist is a GUC variable. If it's true, that means
591 * that we should lock out manual stash modifications until the dump file
592 * has been successfully loaded. If it's false, there's nothing to load,
593 * so we set stashes_ready immediately.
594 *
595 * If this module was not loaded via shared_preload_libraries, then
596 * pg_stash_advice_persist is not a GUC variable, but it will be false,
597 * which leads to the correct behavior.
598 */
600 pg_atomic_test_set_flag(&state->stashes_ready);
601}
static bool pg_atomic_test_set_flag(volatile pg_atomic_flag *ptr)
Definition atomics.h:176
static void pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
Definition atomics.h:448
static void pg_atomic_init_flag(volatile pg_atomic_flag *ptr)
Definition atomics.h:163
#define UINT64CONST(x)
Definition c.h:690
int LWLockNewTrancheId(const char *name)
Definition lwlock.c:562
void LWLockInitialize(LWLock *lock, int tranche_id)
Definition lwlock.c:670
#define InvalidPid
Definition miscadmin.h:32

References DSA_HANDLE_INVALID, DSHASH_HANDLE_INVALID, InvalidPid, LWLockInitialize(), LWLockNewTrancheId(), pg_atomic_init_flag(), pg_atomic_init_u64(), pg_atomic_test_set_flag(), pg_stash_advice_persist, and UINT64CONST.

Referenced by pgsa_attach().

◆ pgsa_is_identifier()

static bool pgsa_is_identifier ( char str)
static

Definition at line 608 of file pg_stash_advice.c.

609{
610 if (*str >= '0' && *str <= '9')
611 return false;
612
613 while (*str != '\0')
614 {
615 char c = *str++;
616
617 if ((c < '0' || c > '9') && (c < 'a' || c > 'z') &&
618 (c < 'A' || c > 'Z') && c != '_')
619 return false;
620 }
621
622 return true;
623}
const char * str
char * c

References str.

Referenced by pgsa_check_stash_name(), and pgsa_check_stash_name_guc().

◆ pgsa_lookup_stash_id()

uint64 pgsa_lookup_stash_id ( char stash_name)

Definition at line 631 of file pg_stash_advice.c.

632{
635
636 /* Search the shared hash table. */
637 stash = dshash_find(pgsa_stash_dshash, stash_name, false);
638 if (stash == NULL)
639 return 0;
640 stash_id = stash->pgsa_stash_id;
642
643 return stash_id;
644}

References dshash_find(), dshash_release_lock(), fb(), and pgsa_stash_dshash.

Referenced by pg_get_advice_stash_contents(), pgsa_advisor(), pgsa_clear_advice_string(), and pgsa_set_advice_string().

◆ pgsa_reset_all_stashes()

void pgsa_reset_all_stashes ( void  )

Definition at line 540 of file pg_stash_advice.c.

541{
543 pgsa_entry *entry;
544
546
547 /* Remove all stashes. */
549 while (dshash_seq_next(&iter) != NULL)
551 dshash_seq_term(&iter);
552
553 /* Remove all entries. */
555 while ((entry = dshash_seq_next(&iter)) != NULL)
556 {
557 if (entry->advice_string != InvalidDsaPointer)
560 }
561 dshash_seq_term(&iter);
562
563 /* Reset the stash ID counter. */
565}

References pgsa_entry::advice_string, Assert, dsa_free(), dshash_delete_current(), dshash_seq_init(), dshash_seq_next(), dshash_seq_term(), fb(), InvalidDsaPointer, pgsa_shared_state::lock, LW_EXCLUSIVE, LWLockHeldByMeInMode(), pgsa_shared_state::next_stash_id, pgsa_dsa_area, pgsa_entry_dshash, pgsa_stash_dshash, pgsa_state, and UINT64CONST.

Referenced by pgsa_read_from_disk().

◆ pgsa_set_advice_string()

void pgsa_set_advice_string ( char stash_name,
int64  queryId,
char advice_string 
)

Definition at line 650 of file pg_stash_advice.c.

651{
652 pgsa_entry *entry;
653 bool found;
658
659 /*
660 * The caller must hold our lock, at least in shared mode. This is
661 * important for two reasons.
662 *
663 * First, it holds off interrupts, so that we can't bail out of this code
664 * after allocating DSA memory for the advice string and before storing
665 * the resulting pointer somewhere that others can find it.
666 *
667 * Second, we need to avoid a race against pgsa_drop_stash(). That
668 * function removes a stash_name->stash_id mapping and all the entries for
669 * that stash_id. Without the lock, there's a race condition no matter
670 * which of those things it does first, because as soon as we've looked up
671 * the stash ID, that whole function can execute before we do the rest of
672 * our work, which would result in us adding an entry for a stash that no
673 * longer exists.
674 */
676
677 /* Look up the stash ID. */
678 if ((stash_id = pgsa_lookup_stash_id(stash_name)) == 0)
681 errmsg("advice stash \"%s\" does not exist", stash_name));
682
683 /* Allocate space for the advice string. */
684 new_dp = dsa_allocate(pgsa_dsa_area, strlen(advice_string) + 1);
685 strcpy(dsa_get_address(pgsa_dsa_area, new_dp), advice_string);
686
687 /* Attempt to insert an entry into the hash table. */
688 memset(&key, 0, sizeof(pgsa_entry_key));
689 key.pgsa_stash_id = stash_id;
690 key.queryId = queryId;
693
694 /*
695 * If it didn't work, bail out, being careful to free the shared memory
696 * we've already allocated before, since error cleanup will not do so.
697 */
698 if (entry == NULL)
699 {
703 errmsg("out of memory"),
704 errdetail("could not insert advice string into shared hash table"));
705 }
706
707 /* Update the entry and release the lock. */
708 old_dp = found ? entry->advice_string : InvalidDsaPointer;
709 entry->advice_string = new_dp;
711
712 /*
713 * We're not safe from leaks yet!
714 *
715 * There's now a pointer to new_dp in the entry that we just updated, but
716 * that means that there's no longer anything pointing to old_dp.
717 */
720
721 /* Bump change count. */
723}
#define dsa_allocate(area, size)
Definition dsa.h:109
#define DsaPointerIsValid(x)
Definition dsa.h:106
void * dshash_find_or_insert_extended(dshash_table *hash_table, const void *key, bool *found, int flags)
Definition dshash.c:442
#define DSHASH_INSERT_NO_OOM
Definition dshash.h:96
int errdetail(const char *fmt,...) pg_attribute_printf(1

References pgsa_entry::advice_string, Assert, pgsa_shared_state::change_count, dsa_allocate, dsa_free(), dsa_get_address(), DsaPointerIsValid, dshash_find_or_insert_extended(), DSHASH_INSERT_NO_OOM, dshash_release_lock(), ereport, errcode(), errdetail(), errmsg, ERROR, fb(), InvalidDsaPointer, pgsa_shared_state::lock, LWLockHeldByMe(), pg_atomic_add_fetch_u64(), pgsa_dsa_area, pgsa_entry_dshash, pgsa_lookup_stash_id(), and pgsa_state.

Referenced by pg_set_stashed_advice(), and pgsa_restore_entries().

◆ pgsa_start_worker()

void pgsa_start_worker ( void  )

Definition at line 729 of file pg_stash_advice.c.

730{
731 BackgroundWorker worker = {0};
733 BgwHandleStatus status;
734 pid_t pid;
735
739 strcpy(worker.bgw_library_name, "pg_stash_advice");
740 strcpy(worker.bgw_function_name, "pg_stash_advice_worker_main");
741 strcpy(worker.bgw_name, "pg_stash_advice worker");
742 strcpy(worker.bgw_type, "pg_stash_advice worker");
743
744 /*
745 * If process_shared_preload_libraries_in_progress = true, we may be in
746 * the postmaster, in which case this will really register the worker, or
747 * we may be in a child process in an EXEC_BACKEND build, in which case it
748 * will silently do nothing (which is the correct behavior).
749 */
751 {
753 return;
754 }
755
756 /*
757 * If process_shared_preload_libraries_in_progress = false, we're being
758 * asked to start the worker after system startup time. In other words,
759 * unless this is single-user mode, we're not in the postmaster, so we
760 * should use RegisterDynamicBackgroundWorker and then wait for startup to
761 * complete. (If we do happen to be in single-user mode, this will error
762 * out, which is fine.)
763 */
764 worker.bgw_notify_pid = MyProcPid;
765 if (!RegisterDynamicBackgroundWorker(&worker, &handle))
768 errmsg("could not register background process"),
769 errhint("You may need to increase \"max_worker_processes\".")));
770 status = WaitForBackgroundWorkerStartup(handle, &pid);
771 if (status != BGWH_STARTED)
774 errmsg("could not start background process"),
775 errhint("More details may be available in the server log.")));
776}
void RegisterBackgroundWorker(BackgroundWorker *worker)
Definition bgworker.c:962
BgwHandleStatus WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
Definition bgworker.c:1235
bool RegisterDynamicBackgroundWorker(BackgroundWorker *worker, BackgroundWorkerHandle **handle)
Definition bgworker.c:1068
BgwHandleStatus
Definition bgworker.h:111
@ BGWH_STARTED
Definition bgworker.h:112
@ BgWorkerStart_ConsistentState
Definition bgworker.h:87
#define BGWORKER_SHMEM_ACCESS
Definition bgworker.h:53
#define BGW_DEFAULT_RESTART_INTERVAL
Definition bgworker.h:91
int errhint(const char *fmt,...) pg_attribute_printf(1
int MyProcPid
Definition globals.c:49
char bgw_function_name[BGW_MAXLEN]
Definition bgworker.h:104
char bgw_name[BGW_MAXLEN]
Definition bgworker.h:98
char bgw_type[BGW_MAXLEN]
Definition bgworker.h:99
BgWorkerStartTime bgw_start_time
Definition bgworker.h:101
pid_t bgw_notify_pid
Definition bgworker.h:107
char bgw_library_name[MAXPGPATH]
Definition bgworker.h:103

References BGW_DEFAULT_RESTART_INTERVAL, BackgroundWorker::bgw_flags, BackgroundWorker::bgw_function_name, BackgroundWorker::bgw_library_name, BackgroundWorker::bgw_name, BackgroundWorker::bgw_notify_pid, BackgroundWorker::bgw_restart_time, BackgroundWorker::bgw_start_time, BackgroundWorker::bgw_type, BGWH_STARTED, BGWORKER_SHMEM_ACCESS, BgWorkerStart_ConsistentState, ereport, errcode(), errhint(), errmsg, ERROR, fb(), MyProcPid, process_shared_preload_libraries_in_progress, RegisterBackgroundWorker(), RegisterDynamicBackgroundWorker(), and WaitForBackgroundWorkerStartup().

Referenced by _PG_init(), and pg_start_stash_advice_worker().

Variable Documentation

◆ pg_stash_advice_mcxt

MemoryContext pg_stash_advice_mcxt
static

Definition at line 61 of file pg_stash_advice.c.

Referenced by pgsa_attach().

◆ pg_stash_advice_persist

bool pg_stash_advice_persist = true

Definition at line 51 of file pg_stash_advice.c.

Referenced by _PG_init(), and pgsa_init_shared_state().

◆ pg_stash_advice_persist_interval

int pg_stash_advice_persist_interval = 30

Definition at line 52 of file pg_stash_advice.c.

Referenced by _PG_init(), and pg_stash_advice_worker_main().

◆ pg_stash_advice_stash_name

char* pg_stash_advice_stash_name = ""
static

Definition at line 50 of file pg_stash_advice.c.

Referenced by _PG_init(), and pgsa_advisor().

◆ pgsa_dsa_area

◆ pgsa_entry_dshash

◆ pgsa_entry_dshash_parameters

dshash_parameters pgsa_entry_dshash_parameters
static
Initial value:
= {
sizeof(pgsa_entry_key),
sizeof(pgsa_entry),
}
void dshash_memcpy(void *dest, const void *src, size_t size, void *arg)
Definition dshash.c:611
dshash_hash dshash_memhash(const void *v, size_t size, void *arg)
Definition dshash.c:602
int dshash_memcmp(const void *a, const void *b, size_t size, void *arg)
Definition dshash.c:593
@ LWTRANCHE_INVALID
Definition lwlock.h:164

Definition at line 40 of file pg_stash_advice.c.

40 {
41 sizeof(pgsa_entry_key),
42 sizeof(pgsa_entry),
46 LWTRANCHE_INVALID /* gets set at runtime */
47};

Referenced by pgsa_attach().

◆ pgsa_stash_dshash

◆ pgsa_stash_dshash_parameters

dshash_parameters pgsa_stash_dshash_parameters
static
Initial value:
= {
sizeof(pgsa_stash),
}
void dshash_strcpy(void *dest, const void *src, size_t size, void *arg)
Definition dshash.c:643
dshash_hash dshash_strhash(const void *v, size_t size, void *arg)
Definition dshash.c:632
int dshash_strcmp(const void *a, const void *b, size_t size, void *arg)
Definition dshash.c:620

Definition at line 31 of file pg_stash_advice.c.

31 {
33 sizeof(pgsa_stash),
37 LWTRANCHE_INVALID /* gets set at runtime */
38};

Referenced by pgsa_attach().

◆ pgsa_state