PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_stash_advice.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_stash_advice.c
4 * core infrastructure for pg_stash_advice contrib module
5 *
6 * Copyright (c) 2016-2026, PostgreSQL Global Development Group
7 *
8 * contrib/pg_stash_advice/pg_stash_advice.c
9 *
10 *-------------------------------------------------------------------------
11 */
12#include "postgres.h"
13
14#include "common/hashfn.h"
15#include "common/string.h"
16#include "miscadmin.h"
17#include "nodes/queryjumble.h"
18#include "pg_plan_advice.h"
19#include "pg_stash_advice.h"
20#include "postmaster/bgworker.h"
22#include "utils/guc.h"
23#include "utils/memutils.h"
24
26
27/* Shared memory hash table parameters */
36
38 sizeof(pgsa_entry_key),
39 sizeof(pgsa_entry),
43 LWTRANCHE_INVALID /* gets set at runtime */
44};
45
46/* GUC variables */
50
51/* Shared memory pointers */
56
57/* Other global variables */
59
60/* Function prototypes */
61static char *pgsa_advisor(PlannerGlobal *glob,
62 Query *parse,
63 const char *query_string,
64 int cursorOptions,
65 ExplainState *es);
66static bool pgsa_check_stash_name_guc(char **newval, void **extra,
68static void pgsa_init_shared_state(void *ptr, void *arg);
69static bool pgsa_is_identifier(char *str);
70
71/* Stash name -> stash ID hash table */
72#define SH_PREFIX pgsa_stash_name_table
73#define SH_ELEMENT_TYPE pgsa_stash_name
74#define SH_KEY_TYPE uint64
75#define SH_KEY pgsa_stash_id
76#define SH_HASH_KEY(tb, key) hash_bytes((const unsigned char *) &(key), sizeof(uint64))
77#define SH_EQUAL(tb, a, b) (a == b)
78#define SH_SCOPE extern
79#define SH_DEFINE
80#include "lib/simplehash.h"
81
82/*
83 * Initialize this module.
84 */
85void
87{
88 void (*add_advisor_fn) (pg_plan_advice_advisor_hook hook);
89
90 /* If compute_query_id = 'auto', we would like query IDs. */
92
93 /* Define our GUCs. */
95 DefineCustomBoolVariable("pg_stash_advice.persist",
96 "Save and restore advice stash contents across restarts.",
97 NULL,
99 true,
101 0,
102 NULL,
103 NULL,
104 NULL);
105 else
107
108 DefineCustomIntVariable("pg_stash_advice.persist_interval",
109 "Interval between advice stash saves, in seconds.",
110 NULL,
112 30,
113 0,
114 3600,
117 NULL,
118 NULL,
119 NULL);
120
121 DefineCustomStringVariable("pg_stash_advice.stash_name",
122 "Name of the advice stash to be used in this session.",
123 NULL,
125 "",
127 0,
129 NULL,
130 NULL);
131
132 MarkGUCPrefixReserved("pg_stash_advice");
133
134 /* Start the background worker for persistence, if enabled. */
137
138 /* Tell pg_plan_advice that we want to provide advice strings. */
139 add_advisor_fn =
140 load_external_function("pg_plan_advice", "pg_plan_advice_add_advisor",
141 true, NULL);
142 (*add_advisor_fn) (pgsa_advisor);
143}
144
145/*
146 * Get the advice string that has been configured for this query, if any,
147 * and return it. Otherwise, return NULL.
148 */
149static char *
151 const char *query_string, int cursorOptions,
152 ExplainState *es)
153{
154 pgsa_entry_key key;
155 pgsa_entry *entry;
156 char *advice_string;
157 uint64 stash_id;
158
159 /*
160 * Exit quickly if the stash name is empty or there's no query ID.
161 */
162 if (pg_stash_advice_stash_name[0] == '\0' || parse->queryId == 0)
163 return NULL;
164
165 /* Attach to dynamic shared memory if not already done. */
166 if (unlikely(pgsa_entry_dshash == NULL))
167 pgsa_attach();
168
169 /* If stash data is still being restored from disk, ignore. */
171 return NULL;
172
173 /*
174 * Translate pg_stash_advice.stash_name to an integer ID.
175 *
176 * pgsa_check_stash_name_guc() has already validated the advice stash
177 * name, so we don't need to call pgsa_check_stash_name() here.
178 */
180 if (stash_id == 0)
181 return NULL;
182
183 /*
184 * Look up the advice string for the given stash ID + query ID.
185 *
186 * If we find an advice string, we copy it into the current memory
187 * context, presumably short-lived, so that we can release the lock on the
188 * dshash entry. pg_plan_advice only needs the value to remain allocated
189 * long enough for it to be parsed, so this should be good enough.
190 */
191 memset(&key, 0, sizeof(pgsa_entry_key));
192 key.pgsa_stash_id = stash_id;
193 key.queryId = parse->queryId;
194 entry = dshash_find(pgsa_entry_dshash, &key, false);
195 if (entry == NULL)
196 return NULL;
197 if (entry->advice_string == InvalidDsaPointer)
198 advice_string = NULL;
199 else
200 advice_string = pstrdup(dsa_get_address(pgsa_dsa_area,
201 entry->advice_string));
203
204 /* If we found an advice string, emit a debug message. */
205 if (advice_string != NULL)
206 elog(DEBUG2, "supplying automatic advice for stash \"%s\", query ID %" PRId64 ": %s",
207 pg_stash_advice_stash_name, key.queryId, advice_string);
208
209 return advice_string;
210}
211
212/*
213 * Attach to various structures in dynamic shared memory.
214 *
215 * This function is designed to be resilient against errors. That is, if it
216 * fails partway through, it should be possible to call it again, repeat no
217 * work already completed, and potentially succeed or at least get further if
218 * whatever caused the previous failure has been corrected.
219 */
220void
222{
223 bool found;
224 MemoryContext oldcontext;
225
226 /*
227 * Create a memory context to make sure that any control structures
228 * allocated in local memory are sufficiently persistent.
229 */
230 if (pg_stash_advice_mcxt == NULL)
232 "pg_stash_advice",
235
236 /* Attach to the fixed-size state object if not already done. */
237 if (pgsa_state == NULL)
238 pgsa_state = GetNamedDSMSegment("pg_stash_advice",
239 sizeof(pgsa_shared_state),
241 &found, NULL);
242
243 /* Attach to the DSA area if not already done. */
244 if (pgsa_dsa_area == NULL)
245 {
246 dsa_handle area_handle;
247
249 area_handle = pgsa_state->area;
250 if (area_handle == DSA_HANDLE_INVALID)
251 {
256 }
257 else
258 {
260 pgsa_dsa_area = dsa_attach(area_handle);
261 }
263 }
264
265 /* Attach to the stash_name->stash_id hash table if not already done. */
266 if (pgsa_stash_dshash == NULL)
267 {
268 dshash_table_handle stash_handle;
269
272 stash_handle = pgsa_state->stash_hash;
273 if (stash_handle == DSHASH_HANDLE_INVALID)
274 {
277 NULL);
281 }
282 else
283 {
287 stash_handle, NULL);
288 }
289 }
290
291 /* Attach to the entry hash table if not already done. */
292 if (pgsa_entry_dshash == NULL)
293 {
294 dshash_table_handle entry_handle;
295
298 entry_handle = pgsa_state->entry_hash;
299 if (entry_handle == DSHASH_HANDLE_INVALID)
300 {
303 NULL);
307 }
308 else
309 {
313 entry_handle, NULL);
314 }
315 }
316
317 /* Restore previous memory context. */
318 MemoryContextSwitchTo(oldcontext);
319}
320
321/*
322 * Error out if the stashes have not been loaded from disk yet.
323 */
324void
326{
329 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
330 errmsg("stash modifications are not allowed because \"%s\" has not been loaded yet",
332}
333
334/*
335 * Check whether an advice stash name is legal, and signal an error if not.
336 *
337 * Keep this in sync with pgsa_check_stash_name_guc, below.
338 */
339void
340pgsa_check_stash_name(char *stash_name)
341{
342 /* Reject empty advice stash name. */
343 if (stash_name[0] == '\0')
345 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
346 errmsg("advice stash name may not be zero length"));
347
348 /* Reject overlong advice stash names. */
349 if (strlen(stash_name) + 1 > NAMEDATALEN)
351 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
352 errmsg("advice stash names may not be longer than %d bytes",
353 NAMEDATALEN - 1));
354
355 /*
356 * Reject non-ASCII advice stash names, since advice stashes are visible
357 * across all databases and the encodings of those databases might differ.
358 */
359 if (!pg_is_ascii(stash_name))
361 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
362 errmsg("advice stash name must not contain non-ASCII characters"));
363
364 /*
365 * Reject things that do not look like identifiers, since the ability to
366 * create an advice stash with non-printable characters or weird symbols
367 * in the name is not likely to be useful to anyone.
368 */
369 if (!pgsa_is_identifier(stash_name))
371 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
372 errmsg("advice stash name must begin with a letter or underscore and contain only letters, digits, and underscores"));
373}
374
375/*
376 * As above, but for the GUC check_hook. We allow the empty string here,
377 * though, as equivalent to disabling the feature.
378 */
379static bool
381{
382 char *stash_name = *newval;
383
384 /* Reject overlong advice stash names. */
385 if (strlen(stash_name) + 1 > NAMEDATALEN)
386 {
387 GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
388 GUC_check_errdetail("advice stash names may not be longer than %d bytes",
389 NAMEDATALEN - 1);
390 return false;
391 }
392
393 /*
394 * Reject non-ASCII advice stash names, since advice stashes are visible
395 * across all databases and the encodings of those databases might differ.
396 */
397 if (!pg_is_ascii(stash_name))
398 {
399 GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
400 GUC_check_errdetail("advice stash name must not contain non-ASCII characters");
401 return false;
402 }
403
404 /*
405 * Reject things that do not look like identifiers, since the ability to
406 * create an advice stash with non-printable characters or weird symbols
407 * in the name is not likely to be useful to anyone.
408 */
409 if (!pgsa_is_identifier(stash_name))
410 {
411 GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
412 GUC_check_errdetail("advice stash name must begin with a letter or underscore and contain only letters, digits, and underscores");
413 return false;
414 }
415
416 return true;
417}
418
419/*
420 * Create an advice stash.
421 */
422void
423pgsa_create_stash(char *stash_name)
424{
425 pgsa_stash *stash;
426 bool found;
427
429
430 /* Create a stash with this name, unless one already exists. */
431 stash = dshash_find_or_insert(pgsa_stash_dshash, stash_name, &found);
432 if (found)
434 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
435 errmsg("advice stash \"%s\" already exists", stash_name));
438
439 /* Bump change count. */
441}
442
443/*
444 * Remove any stored advice string for the given advice stash and query ID.
445 */
446void
447pgsa_clear_advice_string(char *stash_name, int64 queryId)
448{
449 pgsa_entry *entry;
450 pgsa_entry_key key;
451 uint64 stash_id;
452 dsa_pointer old_dp;
453
455
456 /* Translate the stash name to an integer ID. */
457 if ((stash_id = pgsa_lookup_stash_id(stash_name)) == 0)
459 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
460 errmsg("advice stash \"%s\" does not exist", stash_name));
461
462 /*
463 * Look for an existing entry, and free it. But, be sure to save the
464 * pointer to the associated advice string, if any.
465 */
466 memset(&key, 0, sizeof(pgsa_entry_key));
467 key.pgsa_stash_id = stash_id;
468 key.queryId = queryId;
469 entry = dshash_find(pgsa_entry_dshash, &key, true);
470 if (entry == NULL)
471 old_dp = InvalidDsaPointer;
472 else
473 {
474 old_dp = entry->advice_string;
476 }
477
478 /* Now we free the advice string as well, if there was one. */
479 if (old_dp != InvalidDsaPointer)
480 dsa_free(pgsa_dsa_area, old_dp);
481
482 /* Bump change count. */
484}
485
486/*
487 * Drop an advice stash.
488 */
489void
490pgsa_drop_stash(char *stash_name)
491{
492 pgsa_entry *entry;
493 pgsa_stash *stash;
494 dshash_seq_status iterator;
495 uint64 stash_id;
496
498
499 /* Remove the entry for this advice stash. */
500 stash = dshash_find(pgsa_stash_dshash, stash_name, true);
501 if (stash == NULL)
503 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
504 errmsg("advice stash \"%s\" does not exist", stash_name));
505 stash_id = stash->pgsa_stash_id;
507
508 /*
509 * Now remove all the entries. Since pgsa_state->lock must be held at
510 * least in shared mode to insert entries into pgsa_entry_dshash, it
511 * doesn't matter whether we do this before or after deleting the entry
512 * from pgsa_stash_dshash.
513 */
514 dshash_seq_init(&iterator, pgsa_entry_dshash, true);
515 while ((entry = dshash_seq_next(&iterator)) != NULL)
516 {
517 if (stash_id == entry->key.pgsa_stash_id)
518 {
519 if (entry->advice_string != InvalidDsaPointer)
521 dshash_delete_current(&iterator);
522 }
523 }
524 dshash_seq_term(&iterator);
525
526 /* Bump change count. */
528}
529
530/*
531 * Remove all stashes and entries from shared memory.
532 *
533 * This is intended to be called before reloading from a dump file, so that
534 * a failed previous attempt doesn't leave stale data behind.
535 */
536void
538{
540 pgsa_entry *entry;
541
543
544 /* Remove all stashes. */
546 while (dshash_seq_next(&iter) != NULL)
548 dshash_seq_term(&iter);
549
550 /* Remove all entries. */
552 while ((entry = dshash_seq_next(&iter)) != NULL)
553 {
554 if (entry->advice_string != InvalidDsaPointer)
557 }
558 dshash_seq_term(&iter);
559
560 /* Reset the stash ID counter. */
562}
563
564/*
565 * Initialize shared state when first created.
566 */
567static void
569{
571
572 LWLockInitialize(&state->lock,
573 LWLockNewTrancheId("pg_stash_advice_lock"));
574 state->dsa_tranche = LWLockNewTrancheId("pg_stash_advice_dsa");
575 state->stash_tranche = LWLockNewTrancheId("pg_stash_advice_stash");
576 state->entry_tranche = LWLockNewTrancheId("pg_stash_advice_entry");
577 state->next_stash_id = UINT64CONST(1);
579 state->stash_hash = DSHASH_HANDLE_INVALID;
580 state->entry_hash = DSHASH_HANDLE_INVALID;
581 state->bgworker_pid = InvalidPid;
582 pg_atomic_init_flag(&state->stashes_ready);
583 pg_atomic_init_u64(&state->change_count, 0);
584
585 /*
586 * If this module was loaded via shared_preload_libraries, then
587 * pg_stash_advice_persist is a GUC variable. If it's true, that means
588 * that we should lock out manual stash modifications until the dump file
589 * has been successfully loaded. If it's false, there's nothing to load,
590 * so we set stashes_ready immediately.
591 *
592 * If this module was not loaded via shared_preload_libraries, then
593 * pg_stash_advice_persist is not a GUC variable, but it will be false,
594 * which leads to the correct behavior.
595 */
597 pg_atomic_test_set_flag(&state->stashes_ready);
598}
599
600/*
601 * Check whether a string looks like a valid identifier. It must contain only
602 * ASCII identifier characters, and must not begin with a digit.
603 */
604static bool
606{
607 if (*str >= '0' && *str <= '9')
608 return false;
609
610 while (*str != '\0')
611 {
612 char c = *str++;
613
614 if ((c < '0' || c > '9') && (c < 'a' || c > 'z') &&
615 (c < 'A' || c > 'Z') && c != '_')
616 return false;
617 }
618
619 return true;
620}
621
622/*
623 * Look up the integer ID that corresponds to the given stash name.
624 *
625 * Returns 0 if no such stash exists.
626 */
627uint64
628pgsa_lookup_stash_id(char *stash_name)
629{
630 pgsa_stash *stash;
631 uint64 stash_id;
632
633 /* Search the shared hash table. */
634 stash = dshash_find(pgsa_stash_dshash, stash_name, false);
635 if (stash == NULL)
636 return 0;
637 stash_id = stash->pgsa_stash_id;
639
640 return stash_id;
641}
642
643/*
644 * Store a new or updated advice string for the given advice stash and query ID.
645 */
646void
647pgsa_set_advice_string(char *stash_name, int64 queryId, char *advice_string)
648{
649 pgsa_entry *entry;
650 bool found;
651 pgsa_entry_key key;
652 uint64 stash_id;
653 dsa_pointer new_dp;
654 dsa_pointer old_dp;
655
656 /*
657 * The caller must hold our lock, at least in shared mode. This is
658 * important for two reasons.
659 *
660 * First, it holds off interrupts, so that we can't bail out of this code
661 * after allocating DSA memory for the advice string and before storing
662 * the resulting pointer somewhere that others can find it.
663 *
664 * Second, we need to avoid a race against pgsa_drop_stash(). That
665 * function removes a stash_name->stash_id mapping and all the entries for
666 * that stash_id. Without the lock, there's a race condition no matter
667 * which of those things it does first, because as soon as we've looked up
668 * the stash ID, that whole function can execute before we do the rest of
669 * our work, which would result in us adding an entry for a stash that no
670 * longer exists.
671 */
673
674 /* Look up the stash ID. */
675 if ((stash_id = pgsa_lookup_stash_id(stash_name)) == 0)
677 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
678 errmsg("advice stash \"%s\" does not exist", stash_name));
679
680 /* Allocate space for the advice string. */
681 new_dp = dsa_allocate(pgsa_dsa_area, strlen(advice_string) + 1);
682 strcpy(dsa_get_address(pgsa_dsa_area, new_dp), advice_string);
683
684 /* Attempt to insert an entry into the hash table. */
685 memset(&key, 0, sizeof(pgsa_entry_key));
686 key.pgsa_stash_id = stash_id;
687 key.queryId = queryId;
690
691 /*
692 * If it didn't work, bail out, being careful to free the shared memory
693 * we've already allocated before, since error cleanup will not do so.
694 */
695 if (entry == NULL)
696 {
697 dsa_free(pgsa_dsa_area, new_dp);
699 errcode(ERRCODE_OUT_OF_MEMORY),
700 errmsg("out of memory"),
701 errdetail("could not insert advice string into shared hash table"));
702 }
703
704 /* Update the entry and release the lock. */
705 old_dp = found ? entry->advice_string : InvalidDsaPointer;
706 entry->advice_string = new_dp;
708
709 /*
710 * We're not safe from leaks yet!
711 *
712 * There's now a pointer to new_dp in the entry that we just updated, but
713 * that means that there's no longer anything pointing to old_dp.
714 */
715 if (DsaPointerIsValid(old_dp))
716 dsa_free(pgsa_dsa_area, old_dp);
717
718 /* Bump change count. */
720}
721
722/*
723 * Start our worker process.
724 */
725void
727{
728 BackgroundWorker worker = {0};
730 BgwHandleStatus status;
731 pid_t pid;
732
736 strcpy(worker.bgw_library_name, "pg_stash_advice");
737 strcpy(worker.bgw_function_name, "pg_stash_advice_worker_main");
738 strcpy(worker.bgw_name, "pg_stash_advice worker");
739 strcpy(worker.bgw_type, "pg_stash_advice worker");
740
741 /*
742 * If process_shared_preload_libraries_in_progress = true, we may be in
743 * the postmaster, in which case this will really register the worker, or
744 * we may be in a child process in an EXEC_BACKEND build, in which case it
745 * will silently do nothing (which is the correct behavior).
746 */
748 {
750 return;
751 }
752
753 /*
754 * If process_shared_preload_libraries_in_progress = false, we're being
755 * asked to start the worker after system startup time. In other words,
756 * unless this is single-user mode, we're not in the postmaster, so we
757 * should use RegisterDynamicBackgroundWorker and then wait for startup to
758 * complete. (If we do happen to be in single-user mode, this will error
759 * out, which is fine.)
760 */
761 worker.bgw_notify_pid = MyProcPid;
762 if (!RegisterDynamicBackgroundWorker(&worker, &handle))
764 (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
765 errmsg("could not register background process"),
766 errhint("You may need to increase \"max_worker_processes\".")));
767 status = WaitForBackgroundWorkerStartup(handle, &pid);
768 if (status != BGWH_STARTED)
770 (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
771 errmsg("could not start background process"),
772 errhint("More details may be available in the server log.")));
773}
static bool pg_atomic_test_set_flag(volatile pg_atomic_flag *ptr)
Definition atomics.h:181
static bool pg_atomic_unlocked_test_flag(volatile pg_atomic_flag *ptr)
Definition atomics.h:194
static uint64 pg_atomic_add_fetch_u64(volatile pg_atomic_uint64 *ptr, int64 add_)
Definition atomics.h:569
static void pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
Definition atomics.h:453
static void pg_atomic_init_flag(volatile pg_atomic_flag *ptr)
Definition atomics.h:168
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
#define Assert(condition)
Definition c.h:943
int64_t int64
Definition c.h:621
uint64_t uint64
Definition c.h:625
#define unlikely(x)
Definition c.h:438
#define UINT64CONST(x)
Definition c.h:631
void * load_external_function(const char *filename, const char *funcname, bool signalNotFound, void **filehandle)
Definition dfmgr.c:95
dsa_area * dsa_attach(dsa_handle handle)
Definition dsa.c:510
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition dsa.c:957
void dsa_pin_mapping(dsa_area *area)
Definition dsa.c:650
dsa_handle dsa_get_handle(dsa_area *area)
Definition dsa.c:498
void dsa_free(dsa_area *area, dsa_pointer dp)
Definition dsa.c:841
void dsa_pin(dsa_area *area)
Definition dsa.c:990
uint64 dsa_pointer
Definition dsa.h:62
#define dsa_create(tranche_id)
Definition dsa.h:117
#define dsa_allocate(area, size)
Definition dsa.h:109
dsm_handle dsa_handle
Definition dsa.h:136
#define InvalidDsaPointer
Definition dsa.h:78
#define DSA_HANDLE_INVALID
Definition dsa.h:139
#define DsaPointerIsValid(x)
Definition dsa.h:106
void dshash_memcpy(void *dest, const void *src, size_t size, void *arg)
Definition dshash.c:611
void dshash_delete_entry(dshash_table *hash_table, void *entry)
Definition dshash.c:562
void dshash_strcpy(void *dest, const void *src, size_t size, void *arg)
Definition dshash.c:643
void dshash_release_lock(dshash_table *hash_table, void *entry)
Definition dshash.c:579
void dshash_seq_init(dshash_seq_status *status, dshash_table *hash_table, bool exclusive)
Definition dshash.c:659
void * dshash_find(dshash_table *hash_table, const void *key, bool exclusive)
Definition dshash.c:394
dshash_hash dshash_strhash(const void *v, size_t size, void *arg)
Definition dshash.c:632
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
void dshash_seq_term(dshash_seq_status *status)
Definition dshash.c:768
int dshash_strcmp(const void *a, const void *b, size_t size, void *arg)
Definition dshash.c:620
dshash_hash dshash_memhash(const void *v, size_t size, void *arg)
Definition dshash.c:602
void * dshash_seq_next(dshash_seq_status *status)
Definition dshash.c:678
dshash_table * dshash_create(dsa_area *area, const dshash_parameters *params, void *arg)
Definition dshash.c:210
int dshash_memcmp(const void *a, const void *b, size_t size, void *arg)
Definition dshash.c:593
void * dshash_find_or_insert_extended(dshash_table *hash_table, const void *key, bool *found, int flags)
Definition dshash.c:442
void dshash_delete_current(dshash_seq_status *status)
Definition dshash.c:778
#define DSHASH_HANDLE_INVALID
Definition dshash.h:27
dsa_pointer dshash_table_handle
Definition dshash.h:24
#define DSHASH_INSERT_NO_OOM
Definition dshash.h:96
#define dshash_find_or_insert(hash_table, key, found)
Definition dshash.h:109
void * GetNamedDSMSegment(const char *name, size_t size, void(*init_callback)(void *ptr, void *arg), bool *found, void *arg)
Datum arg
Definition elog.c:1323
int errcode(int sqlerrcode)
Definition elog.c:875
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define DEBUG2
Definition elog.h:30
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
int MyProcPid
Definition globals.c:49
void GUC_check_errcode(int sqlerrcode)
Definition guc.c:6666
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
#define newval
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
#define GUC_check_errdetail
Definition guc.h:507
GucSource
Definition guc.h:112
@ 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
const char * str
void parse(int)
Definition parse.c:49
bool LWLockHeldByMe(LWLock *lock)
Definition lwlock.c:1885
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1150
int LWLockNewTrancheId(const char *name)
Definition lwlock.c:562
bool LWLockHeldByMeInMode(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1929
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1767
void LWLockInitialize(LWLock *lock, int tranche_id)
Definition lwlock.c:670
@ LWTRANCHE_INVALID
Definition lwlock.h:164
@ LW_EXCLUSIVE
Definition lwlock.h:104
char * pstrdup(const char *in)
Definition mcxt.c:1781
MemoryContext TopMemoryContext
Definition mcxt.c:166
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define InvalidPid
Definition miscadmin.h:32
bool process_shared_preload_libraries_in_progress
Definition miscinit.c:1788
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
#define NAMEDATALEN
char *(* pg_plan_advice_advisor_hook)(PlannerGlobal *glob, Query *parse, const char *query_string, int cursorOptions, ExplainState *es)
static rewind_source * source
Definition pg_rewind.c:89
void _PG_init(void)
bool pg_stash_advice_persist
static char * pgsa_advisor(PlannerGlobal *glob, Query *parse, const char *query_string, int cursorOptions, ExplainState *es)
dshash_table * pgsa_entry_dshash
dshash_table * pgsa_stash_dshash
static MemoryContext pg_stash_advice_mcxt
PG_MODULE_MAGIC
void pgsa_create_stash(char *stash_name)
void pgsa_drop_stash(char *stash_name)
void pgsa_set_advice_string(char *stash_name, int64 queryId, char *advice_string)
void pgsa_start_worker(void)
static bool pgsa_is_identifier(char *str)
void pgsa_reset_all_stashes(void)
dsa_area * pgsa_dsa_area
uint64 pgsa_lookup_stash_id(char *stash_name)
static dshash_parameters pgsa_stash_dshash_parameters
static char * pg_stash_advice_stash_name
static bool pgsa_check_stash_name_guc(char **newval, void **extra, GucSource source)
void pgsa_attach(void)
void pgsa_check_stash_name(char *stash_name)
pgsa_shared_state * pgsa_state
static dshash_parameters pgsa_entry_dshash_parameters
static void pgsa_init_shared_state(void *ptr, void *arg)
int pg_stash_advice_persist_interval
void pgsa_clear_advice_string(char *stash_name, int64 queryId)
void pgsa_check_lockout(void)
#define PGSA_DUMP_FILE
char * c
void EnableQueryId(void)
bool pg_is_ascii(const char *str)
Definition string.c:132
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
uint64 pgsa_stash_id
pgsa_entry_key key
dsa_pointer advice_string
dshash_table_handle entry_hash
dshash_table_handle stash_hash
pg_atomic_uint64 change_count
pg_atomic_flag stashes_ready
uint64 pgsa_stash_id