PostgreSQL Source Code git master
Loading...
Searching...
No Matches
stashpersist.c File Reference
#include "postgres.h"
#include <sys/stat.h>
#include "common/hashfn.h"
#include "miscadmin.h"
#include "pg_stash_advice.h"
#include "postmaster/bgworker.h"
#include "postmaster/interrupt.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
#include "utils/backend_status.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "lib/simplehash.h"
Include dependency graph for stashpersist.c:

Go to the source code of this file.

Data Structures

struct  pgsa_writer_context
 
struct  pgsa_saved_entry
 
struct  pgsa_saved_stash
 

Macros

#define SH_PREFIX   pgsa_saved_stash_table
 
#define SH_ELEMENT_TYPE   pgsa_saved_stash
 
#define SH_KEY_TYPE   char *
 
#define SH_KEY   name
 
#define SH_HASH_KEY(tb, key)   hash_bytes((const unsigned char *) (key), strlen(key))
 
#define SH_EQUAL(tb, a, b)   (strcmp(a, b) == 0)
 
#define SH_SCOPE   static inline
 
#define SH_DEFINE
 
#define SH_DECLARE
 

Typedefs

typedef struct pgsa_writer_context pgsa_writer_context
 
typedef struct pgsa_saved_entry pgsa_saved_entry
 
typedef struct pgsa_saved_stash pgsa_saved_stash
 

Functions

PGDLLEXPORT void pg_stash_advice_worker_main (Datum main_arg)
 
static void pgsa_append_tsv_escaped_string (StringInfo buf, const char *str)
 
static void pgsa_detach_shmem (int code, Datum arg)
 
static charpgsa_next_tsv_field (char **cursor)
 
static void pgsa_read_from_disk (void)
 
static void pgsa_restore_entries (pgsa_saved_entry *entries, int num_entries)
 
static void pgsa_restore_stashes (pgsa_saved_stash_table_hash *saved_stashes)
 
static void pgsa_unescape_tsv_field (char *str, const char *filename, unsigned lineno)
 
static void pgsa_write_entries (pgsa_writer_context *wctx)
 
static pg_noreturn void pgsa_write_error (pgsa_writer_context *wctx)
 
static void pgsa_write_stashes (pgsa_writer_context *wctx)
 
static void pgsa_write_to_disk (void)
 

Macro Definition Documentation

◆ SH_DECLARE

#define SH_DECLARE

Definition at line 68 of file stashpersist.c.

◆ SH_DEFINE

#define SH_DEFINE

Definition at line 67 of file stashpersist.c.

◆ SH_ELEMENT_TYPE

#define SH_ELEMENT_TYPE   pgsa_saved_stash

Definition at line 61 of file stashpersist.c.

◆ SH_EQUAL

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

Definition at line 65 of file stashpersist.c.

◆ SH_HASH_KEY

#define SH_HASH_KEY (   tb,
  key 
)    hash_bytes((const unsigned char *) (key), strlen(key))

Definition at line 64 of file stashpersist.c.

◆ SH_KEY

#define SH_KEY   name

Definition at line 63 of file stashpersist.c.

◆ SH_KEY_TYPE

#define SH_KEY_TYPE   char *

Definition at line 62 of file stashpersist.c.

◆ SH_PREFIX

#define SH_PREFIX   pgsa_saved_stash_table

Definition at line 60 of file stashpersist.c.

◆ SH_SCOPE

#define SH_SCOPE   static inline

Definition at line 66 of file stashpersist.c.

Typedef Documentation

◆ pgsa_saved_entry

◆ pgsa_saved_stash

◆ pgsa_writer_context

Function Documentation

◆ pg_stash_advice_worker_main()

PGDLLEXPORT void pg_stash_advice_worker_main ( Datum  main_arg)
extern

Definition at line 94 of file stashpersist.c.

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}
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
uint64_t uint64
Definition c.h:625
int64 TimestampTz
Definition timestamp.h:39
#define LOG
Definition elog.h:31
#define DEBUG1
Definition elog.h:30
#define ereport(elevel,...)
Definition elog.h:151
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
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
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_EXCLUSIVE
Definition lwlock.h:104
#define InvalidPid
Definition miscadmin.h:32
void InitializeSessionUserIdStandalone(void)
Definition miscinit.c:841
static char * errmsg
void pgsa_attach(void)
pgsa_shared_state * pgsa_state
int pg_stash_advice_persist_interval
#define pqsignal
Definition port.h:547
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 void pgsa_read_from_disk(void)
static void pgsa_write_to_disk(void)
pg_atomic_uint64 change_count
pg_atomic_flag stashes_ready
#define TimestampTzPlusMilliseconds(tz, ms)
Definition timestamp.h:85
#define PG_WAIT_EXTENSION
#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

References BackgroundWorkerUnblockSignals(), before_shmem_exit(), pgsa_shared_state::bgworker_pid, pgsa_shared_state::change_count, ConfigReloadPending, DEBUG1, ereport, errmsg, fb(), GetCurrentTimestamp(), InitializeSessionUserIdStandalone(), InvalidPid, pgsa_shared_state::lock, LOG, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MyLatch, MyProcPid, pg_atomic_read_u64(), pg_atomic_test_set_flag(), pg_atomic_unlocked_test_flag(), pg_stash_advice_persist_interval, PG_WAIT_EXTENSION, PGC_SIGHUP, pgsa_attach(), pgsa_detach_shmem(), pgsa_read_from_disk(), pgsa_state, pgsa_write_to_disk(), pgstat_beinit(), pgstat_bestart_final(), pgstat_bestart_initial(), pqsignal, ProcessConfigFile(), procsignal_sigusr1_handler(), ResetLatch(), ShutdownRequestPending, SIGHUP, SignalHandlerForConfigReload(), SignalHandlerForShutdownRequest(), SIGUSR1, pgsa_shared_state::stashes_ready, TimestampDifferenceMilliseconds(), TimestampTzPlusMilliseconds, WaitLatch(), WL_EXIT_ON_PM_DEATH, WL_LATCH_SET, and WL_TIMEOUT.

◆ pgsa_append_tsv_escaped_string()

static void pgsa_append_tsv_escaped_string ( StringInfo  buf,
const char str 
)
static

Definition at line 572 of file stashpersist.c.

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}
const char * str
static char buf[DEFAULT_XLOG_SEG_SIZE]
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition stringinfo.c:242

References appendStringInfoChar(), appendStringInfoString(), buf, and str.

Referenced by pgsa_write_entries().

◆ pgsa_detach_shmem()

◆ pgsa_next_tsv_field()

static char * pgsa_next_tsv_field ( char **  cursor)
static

Definition at line 604 of file stashpersist.c.

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}
return str start
Definition type.h:138

References fb(), and start.

Referenced by pgsa_read_from_disk().

◆ pgsa_read_from_disk()

static void pgsa_read_from_disk ( void  )
static

Definition at line 233 of file stashpersist.c.

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}
#define Assert(condition)
Definition c.h:943
int64_t int64
Definition c.h:621
int errcode_for_file_access(void)
Definition elog.c:897
int errcode(int sqlerrcode)
Definition elog.c:874
#define ERROR
Definition elog.h:39
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
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
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
#define ERRCODE_DATA_CORRUPTED
dshash_table * pgsa_entry_dshash
void pgsa_reset_all_stashes(void)
#define PGSA_DUMP_FILE
static char * pgsa_next_tsv_field(char **cursor)
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_restore_stashes(pgsa_saved_stash_table_hash *saved_stashes)
char * stash_name
char * advice_string
int64 queryId
const char * name
#define fstat
Definition win32_port.h:73

References pgsa_saved_entry::advice_string, AllocateFile(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, CurrentMemoryContext, ereport, errcode(), ERRCODE_DATA_CORRUPTED, errcode_for_file_access(), errmsg, ERROR, fb(), FreeFile(), fstat, pgsa_shared_state::lock, LOG, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MCXT_ALLOC_HUGE, MemoryContextDelete(), MemoryContextSwitchTo(), name, palloc(), palloc_extended(), PGSA_DUMP_FILE, pgsa_entry_dshash, pgsa_next_tsv_field(), pgsa_reset_all_stashes(), pgsa_restore_entries(), pgsa_restore_stashes(), pgsa_state, pgsa_unescape_tsv_field(), pgsa_saved_entry::queryId, repalloc(), and pgsa_saved_entry::stash_name.

Referenced by pg_stash_advice_worker_main().

◆ pgsa_restore_entries()

static void pgsa_restore_entries ( pgsa_saved_entry entries,
int  num_entries 
)
static

Definition at line 626 of file stashpersist.c.

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}
#define DEBUG2
Definition elog.h:29
int i
Definition isn.c:77
@ LW_SHARED
Definition lwlock.h:105
void pgsa_set_advice_string(char *stash_name, int64 queryId, char *advice_string)

References DEBUG2, ereport, errmsg, fb(), i, pgsa_shared_state::lock, LW_SHARED, LWLockAcquire(), LWLockRelease(), pgsa_set_advice_string(), and pgsa_state.

Referenced by pgsa_read_from_disk().

◆ pgsa_restore_stashes()

static void pgsa_restore_stashes ( pgsa_saved_stash_table_hash saved_stashes)
static

◆ pgsa_unescape_tsv_field()

static void pgsa_unescape_tsv_field ( char str,
const char filename,
unsigned  lineno 
)
static

Definition at line 669 of file stashpersist.c.

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}
#define likely(x)
Definition c.h:437
static char * filename
Definition pg_dumpall.c:133

References ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg, ERROR, fb(), filename, likely, and str.

Referenced by pgsa_read_from_disk().

◆ pgsa_write_entries()

static void pgsa_write_entries ( pgsa_writer_context wctx)
static

Definition at line 722 of file stashpersist.c.

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}
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
dsa_area * pgsa_dsa_area
static pg_noreturn void pgsa_write_error(pgsa_writer_context *wctx)
static void pgsa_append_tsv_escaped_string(StringInfo buf, const char *str)
void resetStringInfo(StringInfo str)
Definition stringinfo.c:126
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
int64 queryId
uint64 pgsa_stash_id
pgsa_entry_key key
dsa_pointer advice_string

References pgsa_entry::advice_string, appendStringInfo(), appendStringInfoChar(), dsa_get_address(), dshash_seq_init(), dshash_seq_next(), dshash_seq_term(), fb(), InvalidDsaPointer, pgsa_entry::key, pgsa_stash_name::name, pgsa_append_tsv_escaped_string(), pgsa_dsa_area, pgsa_entry_dshash, pgsa_entry_key::pgsa_stash_id, pgsa_write_error(), pgsa_entry_key::queryId, and resetStringInfo().

Referenced by pgsa_write_to_disk().

◆ pgsa_write_error()

static void pgsa_write_error ( pgsa_writer_context wctx)
static

Definition at line 760 of file stashpersist.c.

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}

References ereport, errcode_for_file_access(), errmsg, ERROR, fb(), and FreeFile().

Referenced by pgsa_write_entries(), and pgsa_write_stashes().

◆ pgsa_write_stashes()

static void pgsa_write_stashes ( pgsa_writer_context wctx)
static

Definition at line 777 of file stashpersist.c.

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}
char * pstrdup(const char *in)
Definition mcxt.c:1781
dshash_table * pgsa_stash_dshash

References appendStringInfo(), Assert, dshash_seq_init(), dshash_seq_next(), dshash_seq_term(), fb(), pgsa_stash_name::name, pgsa_stash_dshash, pgsa_write_error(), pstrdup(), and resetStringInfo().

Referenced by pgsa_write_to_disk().

◆ pgsa_write_to_disk()

static void pgsa_write_to_disk ( void  )
static

Definition at line 498 of file stashpersist.c.

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}
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition fd.c:783
#define MAXPGPATH
#define snprintf
Definition port.h:260
static void pgsa_write_entries(pgsa_writer_context *wctx)
static void pgsa_write_stashes(pgsa_writer_context *wctx)
void initStringInfo(StringInfo str)
Definition stringinfo.c:97

References AllocateFile(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, CurrentMemoryContext, DEBUG1, durable_rename(), ereport, errcode_for_file_access(), errmsg, ERROR, fb(), FreeFile(), initStringInfo(), LOG, MAXPGPATH, MemoryContextDelete(), MemoryContextSwitchTo(), PGSA_DUMP_FILE, pgsa_entry_dshash, pgsa_write_entries(), pgsa_write_stashes(), and snprintf.

Referenced by pg_stash_advice_worker_main().