PostgreSQL Source Code git master
Loading...
Searching...
No Matches
stashpersist.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * stashpersist.c
4 * Persistence support for pg_stash_advice.
5 *
6 * Copyright (c) 2016-2026, PostgreSQL Global Development Group
7 *
8 * contrib/pg_stash_advice/stashpersist.c
9 *
10 *-------------------------------------------------------------------------
11 */
12#include "postgres.h"
13
14#include <sys/stat.h>
15
16#include "common/hashfn.h"
17#include "miscadmin.h"
18#include "pg_stash_advice.h"
19#include "postmaster/bgworker.h"
21#include "storage/fd.h"
22#include "storage/ipc.h"
23#include "storage/latch.h"
24#include "storage/proc.h"
25#include "storage/procsignal.h"
27#include "utils/guc.h"
28#include "utils/memutils.h"
29#include "utils/timestamp.h"
30
39
40/*
41 * A parsed entry line, with pointers into the slurp buffer.
42 */
49
50/*
51 * simplehash for detecting duplicate stash names during parsing.
52 * Keyed by stash name (char *), pointing into the slurp buffer.
53 */
59
60#define SH_PREFIX pgsa_saved_stash_table
61#define SH_ELEMENT_TYPE pgsa_saved_stash
62#define SH_KEY_TYPE char *
63#define SH_KEY name
64#define SH_HASH_KEY(tb, key) hash_bytes((const unsigned char *) (key), strlen(key))
65#define SH_EQUAL(tb, a, b) (strcmp(a, b) == 0)
66#define SH_SCOPE static inline
67#define SH_DEFINE
68#define SH_DECLARE
69#include "lib/simplehash.h"
70
72static void pgsa_append_tsv_escaped_string(StringInfo buf, const char *str);
73static void pgsa_detach_shmem(int code, Datum arg);
74static char *pgsa_next_tsv_field(char **cursor);
75static void pgsa_read_from_disk(void);
76static void pgsa_restore_entries(pgsa_saved_entry *entries, int num_entries);
78static void pgsa_unescape_tsv_field(char *str, const char *filename,
79 unsigned lineno);
83static void pgsa_write_to_disk(void);
84
85/*
86 * Background worker entry point for pg_stash_advice persistence.
87 *
88 * On startup, if load_from_disk_pending is set, we load previously saved
89 * stash data from disk. Then we enter a loop, periodically checking whether
90 * any changes have been made (via the change_count atomic counter) and
91 * writing them to disk. On shutdown, we perform a final write.
92 */
93PGDLLEXPORT void
95{
98
99 /* Establish signal handlers; once that's done, unblock signals. */
104
105 /* Log a debug message */
107 errmsg("pg_stash_advice worker started"));
108
109 /* Set up session user so pgstat can report it. */
111
112 /* Report this worker in pg_stat_activity. */
116
117 /* Attach to shared memory structures. */
118 pgsa_attach();
119
120 /* Set on-detach hook so that our PID will be cleared on exit. */
122
123 /*
124 * Store our PID in shared memory, unless there's already another worker
125 * running, in which case just exit.
126 */
129 {
131 ereport(LOG,
132 (errmsg("pg_stash_advice worker is already running under PID %d",
133 (int) pgsa_state->bgworker_pid)));
134 return;
135 }
138
139 /*
140 * If pg_stash_advice.persist was set to true during
141 * process_shared_preload_libraries() and the data has not yet been
142 * successfully loaded, load it now.
143 */
145 {
148 }
149
150 /* Note the current change count so we can detect future changes. */
152
153 /* Periodically write to disk until terminated. */
155 {
156 /* In case of a SIGHUP, just reload the configuration. */
158 {
159 ConfigReloadPending = false;
161 }
162
164 {
165 /* Only writing at shutdown, so just wait forever. */
168 -1L,
170 }
171 else
172 {
174 long delay_in_ms;
176
177 /* Compute when the next write should happen. */
184
185 /*
186 * When we reach next_write_time, we always update last_write_time
187 * (which is really the time at which we last considered writing),
188 * but we only actually write to disk if something has changed.
189 */
190 if (delay_in_ms <= 0)
191 {
195 {
198 }
200 continue;
201 }
202
203 /* Sleep until the next write time. */
208 }
209
211 }
212
213 /* Write one last time before exiting. */
215}
216
217/*
218 * Clear our PID from shared memory on exit.
219 */
220static void
228
229/*
230 * Load advice stash data from a dump file on disk, if there is one.
231 */
232static void
234{
235 struct stat statbuf;
236 FILE *file;
237 char *filebuf;
238 size_t nread;
239 char *p;
240 unsigned lineno;
242 int num_stashes = 0;
243 pgsa_saved_entry *entries;
244 int num_entries = 0;
245 int max_entries = 64;
248
250
251 /*
252 * Clear any existing shared memory state.
253 *
254 * Normally, there won't be any, but if this function was called before
255 * and failed after beginning to apply changes to shared memory, then we
256 * need to get rid of any entries created at that time before trying
257 * again.
258 */
262
263 /* Open the dump file. If it doesn't exist, we're done. */
264 file = AllocateFile(PGSA_DUMP_FILE, "r");
265 if (!file)
266 {
267 if (errno == ENOENT)
268 return;
271 errmsg("could not open file \"%s\": %m", PGSA_DUMP_FILE)));
272 }
273
274 /* Use a temporary context for all parse-phase allocations. */
276 "pg_stash_advice load",
279
280 /* Figure out how long the file is. */
281 if (fstat(fileno(file), &statbuf) != 0)
284 errmsg("could not stat file \"%s\": %m", PGSA_DUMP_FILE)));
285
286 /*
287 * Slurp the entire file into memory all at once.
288 *
289 * We could avoid this by reading the file incrementally and applying
290 * changes to pgsa_stash_dshash and pgsa_entry_dshash as we go. Given the
291 * lockout mechanism implemented by stashes_ready, that shouldn't have any
292 * user-visible behavioral consequences, but it would consume shared
293 * memory to no benefit. It seems better to buffer everything in private
294 * memory first, and then only apply the changes once the file has been
295 * successfully parsed in its entirety.
296 *
297 * That also has the advantage of possibly being more future-proof: if we
298 * decide to remove the stashes_ready mechanism in the future, or say
299 * allow for multiple save files, fully validating the file before
300 * applying any changes will become much more important.
301 *
302 * Of course, this approach does have one major disadvantage, which is
303 * that we'll temporarily use about twice as much memory as we're
304 * ultimately going to need, but that seems like it shouldn't be a problem
305 * in practice. If there's so much stashed advice that parsing the disk
306 * file runs us out of memory, something has gone terribly wrong. In that
307 * situation, there probably also isn't enough free memory for the
308 * workload that the advice is attempting to manipulate to run
309 * successfully.
310 */
312 nread = fread(filebuf, 1, statbuf.st_size, file);
313 if (ferror(file))
316 errmsg("could not read file \"%s\": %m", PGSA_DUMP_FILE)));
317 FreeFile(file);
318 filebuf[nread] = '\0';
319
320 /* Initial memory allocations. */
322 entries = palloc(max_entries * sizeof(pgsa_saved_entry));
323
324 /*
325 * For memory and CPU efficiency, we parse the file in place. The end of
326 * each line gets replaced with a NUL byte, and then the end of each field
327 * within a line gets the same treatment. The advice string is unescaped
328 * in place, and stash names and query IDs can't contain any special
329 * characters. All of the resulting pointers point right back into the
330 * buffer; we only need additional memory to grow the 'entries' array and
331 * the 'saved_stashes' hash table.
332 */
333 for (p = filebuf, lineno = 1; *p != '\0'; lineno++)
334 {
335 char *cursor = p;
336 char *eol;
337 char *line_type;
338
339 /* Find end of line and NUL-terminate. */
340 eol = strchr(p, '\n');
341 if (eol != NULL)
342 {
343 *eol = '\0';
344 p = eol + 1;
345 if (eol > cursor && eol[-1] == '\r')
346 eol[-1] = '\0';
347 }
348 else
349 p += strlen(p);
350
351 /* Skip empty lines. */
352 if (*cursor == '\0')
353 continue;
354
355 /* First field is the type of line, either "stash" or "entry". */
357 if (strcmp(line_type, "stash") == 0)
358 {
359 char *name;
360 bool found;
361
362 /* Second field should be the stash name. */
364 if (name == NULL || *name == '\0')
367 errmsg("syntax error in file \"%s\" line %u: expected stash name",
368 PGSA_DUMP_FILE, lineno)));
369
370 /* No further fields are expected. */
371 if (*cursor != '\0')
374 errmsg("syntax error in file \"%s\" line %u: expected end of line",
375 PGSA_DUMP_FILE, lineno)));
376
377 /* Duplicate check. */
379 if (found)
382 errmsg("syntax error in file \"%s\" line %u: duplicate stash name \"%s\"",
383 PGSA_DUMP_FILE, lineno, name)));
384 num_stashes++;
385 }
386 else if (strcmp(line_type, "entry") == 0)
387 {
388 char *stash_name;
389 char *queryid_str;
390 char *advice_str;
391 char *endptr;
392 int64 queryId;
393
394 /* Second field should be the stash name. */
395 stash_name = pgsa_next_tsv_field(&cursor);
396 if (stash_name == NULL)
399 errmsg("syntax error in file \"%s\" line %u: expected stash name",
400 PGSA_DUMP_FILE, lineno)));
401
402 /* Third field should be the query ID. */
404 if (queryid_str == NULL)
407 errmsg("syntax error in file \"%s\" line %u: expected query ID",
408 PGSA_DUMP_FILE, lineno)));
409
410 /* Fourth field should be the advice string. */
412 if (advice_str == NULL)
415 errmsg("syntax error in file \"%s\" line %u: expected advice string",
416 PGSA_DUMP_FILE, lineno)));
417
418 /* No further fields are expected. */
419 if (*cursor != '\0')
422 errmsg("syntax error in file \"%s\" line %u: expected end of line",
423 PGSA_DUMP_FILE, lineno)));
424
425 /* Make sure the stash is one we've actually seen. */
427 stash_name) == NULL)
430 errmsg("syntax error in file \"%s\" line %u: unknown stash \"%s\"",
431 PGSA_DUMP_FILE, lineno, stash_name)));
432
433 /* Parse the query ID. */
434 errno = 0;
435 queryId = strtoll(queryid_str, &endptr, 10);
436 if (*endptr != '\0' || errno != 0 || queryid_str == endptr ||
437 queryId == 0)
440 errmsg("syntax error in file \"%s\" line %u: invalid query ID \"%s\"",
441 PGSA_DUMP_FILE, lineno, queryid_str)));
442
443 /* Unescape the advice string. */
445
446 /* Append to the entry array. */
447 if (num_entries >= max_entries)
448 {
449 max_entries *= 2;
450 entries = repalloc(entries,
451 max_entries * sizeof(pgsa_saved_entry));
452 }
453 entries[num_entries].stash_name = stash_name;
454 entries[num_entries].queryId = queryId;
455 entries[num_entries].advice_string = advice_str;
456 num_entries++;
457 }
458 else
459 {
462 errmsg("syntax error in file \"%s\" line %u: unrecognized line type",
463 PGSA_DUMP_FILE, lineno)));
464 }
465 }
466
467 /*
468 * Parsing succeeded. Apply everything to shared memory.
469 *
470 * At this point, we know that the file we just read is fully valid, but
471 * it's still possible for this to fail if, for example, DSA memory cannot
472 * be allocated. If that happens, the worker will die, the postmaster will
473 * eventually restart it, and we'll try again after clearing any data that
474 * we did manage to put into shared memory. (Note that we call
475 * pgsa_reset_all_stashes() at the top of this function.)
476 */
478 pgsa_restore_entries(entries, num_entries);
479
480 /* Hooray, it worked! Notify the user. */
481 ereport(LOG,
482 (errmsg("loaded %d advice stashes and %d entries from \"%s\"",
483 num_stashes, num_entries, PGSA_DUMP_FILE)));
484
485 /* Clean up. */
488}
489
490/*
491 * Write all advice stash data to disk.
492 *
493 * The file format is a simple TSV with a line-type prefix:
494 * stash\tstash_name
495 * entry\tstash_name\tquery_id\tadvice_string
496 */
497static void
499{
503
505
506 /* Use a temporary context so all allocations are freed at the end. */
508 "pg_stash_advice dump",
511
512 /* Set up the writer context. */
513 snprintf(wctx.pathname, MAXPGPATH, "%s.tmp", PGSA_DUMP_FILE);
514 wctx.file = AllocateFile(wctx.pathname, "w");
515 if (!wctx.file)
518 errmsg("could not open file \"%s\": %m", wctx.pathname)));
520 initStringInfo(&wctx.buf);
521
522 /* Write stash lines, then entry lines. */
525
526 /*
527 * If nothing was written, remove both the temp file and any existing dump
528 * file rather than installing a zero-length file.
529 */
530 if (wctx.nhash->members == 0)
531 {
533 errmsg("there are no advice stashes to save"));
534 FreeFile(wctx.file);
535 unlink(wctx.pathname);
536 if (unlink(PGSA_DUMP_FILE) == 0)
538 errmsg("removed \"%s\"", PGSA_DUMP_FILE));
539 }
540 else
541 {
542 if (FreeFile(wctx.file) != 0)
543 {
544 int save_errno = errno;
545
546 unlink(wctx.pathname);
550 errmsg("could not close file \"%s\": %m",
551 wctx.pathname)));
552 }
554
555 ereport(LOG,
556 errmsg("saved %d advice stashes and %d entries to \"%s\"",
557 (int) wctx.nhash->members, wctx.entries_written,
559 }
560
563}
564
565/*
566 * Append the TSV-escaped form of str to buf.
567 *
568 * Backslash, tab, newline, and carriage return are escaped with backslash
569 * sequences. All other characters are passed through unchanged.
570 */
571static void
573{
574 for (const char *p = str; *p != '\0'; p++)
575 {
576 switch (*p)
577 {
578 case '\\':
580 break;
581 case '\t':
583 break;
584 case '\n':
586 break;
587 case '\r':
589 break;
590 default:
592 break;
593 }
594 }
595}
596
597/*
598 * Extract the next tab-delimited field from *cursor.
599 *
600 * The tab delimiter is replaced with '\0' and *cursor is advanced past it.
601 * If *cursor already points to '\0' (no more fields), returns NULL.
602 */
603static char *
605{
606 char *start = *cursor;
607 char *p = start;
608
609 if (*p == '\0')
610 return NULL;
611
612 while (*p != '\0' && *p != '\t')
613 p++;
614
615 if (*p == '\t')
616 *p++ = '\0';
617
618 *cursor = p;
619 return start;
620}
621
622/*
623 * Insert entries into shared memory from the parsed entry array.
624 */
625static void
626pgsa_restore_entries(pgsa_saved_entry *entries, int num_entries)
627{
629 for (int i = 0; i < num_entries; i++)
630 {
632 errmsg("restoring advice stash entry for \"%s\", query ID %" PRId64,
633 entries[i].stash_name, entries[i].queryId));
634 pgsa_set_advice_string(entries[i].stash_name,
635 entries[i].queryId,
636 entries[i].advice_string);
637 }
639}
640
641/*
642 * Create stashes in shared memory from the parsed stash hash table.
643 */
644static void
661
662/*
663 * Unescape a TSV field in place.
664 *
665 * Recognized escape sequences are \\, \t, \n, and \r. A trailing backslash
666 * or an unrecognized escape sequence is a syntax error.
667 */
668static void
669pgsa_unescape_tsv_field(char *str, const char *filename, unsigned lineno)
670{
671 char *src = str;
672 char *dst = str;
673
674 while (*src != '\0')
675 {
676 /* Just pass through anything that's not a backslash-escape. */
677 if (likely(*src != '\\'))
678 {
679 *dst++ = *src++;
680 continue;
681 }
682
683 /* Check what sort of escape we've got. */
684 switch (src[1])
685 {
686 case '\\':
687 *dst++ = '\\';
688 break;
689 case 't':
690 *dst++ = '\t';
691 break;
692 case 'n':
693 *dst++ = '\n';
694 break;
695 case 'r':
696 *dst++ = '\r';
697 break;
698 case '\0':
701 errmsg("syntax error in file \"%s\" line %u: trailing backslash",
702 filename, lineno)));
703 break;
704 default:
707 errmsg("syntax error in file \"%s\" line %u: unrecognized escape \"\\%c\"",
708 filename, lineno, src[1])));
709 break;
710 }
711
712 /* We consumed the backslash and the following character. */
713 src += 2;
714 }
715 *dst = '\0';
716}
717
718/*
719 * Write an entry line for each advice entry.
720 */
721static void
723{
725 pgsa_entry *entry;
726
727 dshash_seq_init(&iter, pgsa_entry_dshash, false);
728 while ((entry = dshash_seq_next(&iter)) != NULL)
729 {
731 char *advice_string;
732
733 if (entry->advice_string == InvalidDsaPointer)
734 continue;
735
737 entry->key.pgsa_stash_id);
738 if (n == NULL)
739 continue; /* orphan entry, skip */
740
741 advice_string = dsa_get_address(pgsa_dsa_area, entry->advice_string);
742
743 resetStringInfo(&wctx->buf);
744 appendStringInfo(&wctx->buf, "entry\t%s\t%" PRId64 "\t",
745 n->name, entry->key.queryId);
746 pgsa_append_tsv_escaped_string(&wctx->buf, advice_string);
747 appendStringInfoChar(&wctx->buf, '\n');
748 fwrite(wctx->buf.data, 1, wctx->buf.len, wctx->file);
749 if (ferror(wctx->file))
751 wctx->entries_written++;
752 }
753 dshash_seq_term(&iter);
754}
755
756/*
757 * Clean up and report a write error. Does not return.
758 */
759static void
761{
762 int save_errno = errno;
763
764 FreeFile(wctx->file);
765 unlink(wctx->pathname);
769 errmsg("could not write to file \"%s\": %m", wctx->pathname)));
770}
771
772/*
773 * Write a stash line for each advice stash, and populate the ID-to-name
774 * hash table for use by pgsa_write_entries.
775 */
776static void
778{
781
782 dshash_seq_init(&iter, pgsa_stash_dshash, false);
783 while ((stash = dshash_seq_next(&iter)) != NULL)
784 {
786 bool found;
787
788 n = pgsa_stash_name_table_insert(wctx->nhash, stash->pgsa_stash_id,
789 &found);
790 Assert(!found);
791 n->name = pstrdup(stash->name);
792
793 resetStringInfo(&wctx->buf);
794 appendStringInfo(&wctx->buf, "stash\t%s\n", n->name);
795 fwrite(wctx->buf.data, 1, wctx->buf.len, wctx->file);
796 if (ferror(wctx->file))
798 }
799 dshash_seq_term(&iter);
800}
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_read_u64(volatile pg_atomic_uint64 *ptr)
Definition atomics.h:467
long TimestampDifferenceMilliseconds(TimestampTz start_time, TimestampTz stop_time)
Definition timestamp.c:1751
TimestampTz GetCurrentTimestamp(void)
Definition timestamp.c:1639
void pgstat_bestart_initial(void)
void pgstat_beinit(void)
void pgstat_bestart_final(void)
void BackgroundWorkerUnblockSignals(void)
Definition bgworker.c:949
#define likely(x)
Definition c.h:437
#define pg_noreturn
Definition c.h:190
#define Assert(condition)
Definition c.h:943
int64_t int64
Definition c.h:621
#define PGDLLEXPORT
Definition c.h:1436
uint64_t uint64
Definition c.h:625
uint32_t uint32
Definition c.h:624
int64 TimestampTz
Definition timestamp.h:39
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition dsa.c:957
#define InvalidDsaPointer
Definition dsa.h:78
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
Datum arg
Definition elog.c:1322
int errcode_for_file_access(void)
Definition elog.c:897
int errcode(int sqlerrcode)
Definition elog.c:874
#define LOG
Definition elog.h:31
#define DEBUG2
Definition elog.h:29
#define DEBUG1
Definition elog.h:30
#define ERROR
Definition elog.h:39
#define ereport(elevel,...)
Definition elog.h:151
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition fd.c:783
int FreeFile(FILE *file)
Definition fd.c:2827
FILE * AllocateFile(const char *name, const char *mode)
Definition fd.c:2628
#define MCXT_ALLOC_HUGE
Definition fe_memutils.h:28
int MyProcPid
Definition globals.c:49
struct Latch * MyLatch
Definition globals.c:65
void ProcessConfigFile(GucContext context)
Definition guc-file.l:120
@ PGC_SIGHUP
Definition guc.h:75
return str start
const char * str
void SignalHandlerForShutdownRequest(SIGNAL_ARGS)
Definition interrupt.c:104
volatile sig_atomic_t ShutdownRequestPending
Definition interrupt.c:28
volatile sig_atomic_t ConfigReloadPending
Definition interrupt.c:27
void SignalHandlerForConfigReload(SIGNAL_ARGS)
Definition interrupt.c:61
void before_shmem_exit(pg_on_exit_callback function, Datum arg)
Definition ipc.c:344
int i
Definition isn.c:77
void ResetLatch(Latch *latch)
Definition latch.c:374
int WaitLatch(Latch *latch, int wakeEvents, long timeout, uint32 wait_event_info)
Definition latch.c:172
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1150
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1767
@ LW_SHARED
Definition lwlock.h:105
@ LW_EXCLUSIVE
Definition lwlock.h:104
char * pstrdup(const char *in)
Definition mcxt.c:1781
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
void * palloc_extended(Size size, int flags)
Definition mcxt.c:1439
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define InvalidPid
Definition miscadmin.h:32
void InitializeSessionUserIdStandalone(void)
Definition miscinit.c:841
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
#define ERRCODE_DATA_CORRUPTED
#define MAXPGPATH
static char * filename
Definition pg_dumpall.c:133
dshash_table * pgsa_entry_dshash
dshash_table * pgsa_stash_dshash
void pgsa_create_stash(char *stash_name)
void pgsa_set_advice_string(char *stash_name, int64 queryId, char *advice_string)
void pgsa_reset_all_stashes(void)
dsa_area * pgsa_dsa_area
void pgsa_attach(void)
pgsa_shared_state * pgsa_state
int pg_stash_advice_persist_interval
#define PGSA_DUMP_FILE
static char buf[DEFAULT_XLOG_SEG_SIZE]
#define pqsignal
Definition port.h:547
#define snprintf
Definition port.h:260
uint64_t Datum
Definition postgres.h:70
static int fb(int x)
void procsignal_sigusr1_handler(SIGNAL_ARGS)
Definition procsignal.c:688
static void pgsa_detach_shmem(int code, Datum arg)
static pg_noreturn void pgsa_write_error(pgsa_writer_context *wctx)
static char * pgsa_next_tsv_field(char **cursor)
static void pgsa_append_tsv_escaped_string(StringInfo buf, const char *str)
static void pgsa_restore_entries(pgsa_saved_entry *entries, int num_entries)
static void pgsa_unescape_tsv_field(char *str, const char *filename, unsigned lineno)
static void pgsa_read_from_disk(void)
PGDLLEXPORT void pg_stash_advice_worker_main(Datum main_arg)
static void pgsa_restore_stashes(pgsa_saved_stash_table_hash *saved_stashes)
static void pgsa_write_to_disk(void)
static void pgsa_write_entries(pgsa_writer_context *wctx)
static void pgsa_write_stashes(pgsa_writer_context *wctx)
void resetStringInfo(StringInfo str)
Definition stringinfo.c:126
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition stringinfo.c:242
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
Definition type.h:138
int64 queryId
uint64 pgsa_stash_id
pgsa_entry_key key
dsa_pointer advice_string
char * stash_name
char * advice_string
int64 queryId
pg_atomic_uint64 change_count
pg_atomic_flag stashes_ready
char pathname[MAXPGPATH]
StringInfoData buf
pgsa_stash_name_table_hash * nhash
#define TimestampTzPlusMilliseconds(tz, ms)
Definition timestamp.h:85
#define PG_WAIT_EXTENSION
const char * name
#define WL_TIMEOUT
#define WL_EXIT_ON_PM_DEATH
#define WL_LATCH_SET
#define SIGHUP
Definition win32_port.h:158
#define SIGUSR1
Definition win32_port.h:170
#define fstat
Definition win32_port.h:73