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:1765
TimestampTz GetCurrentTimestamp(void)
Definition timestamp.c:1649
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:32
#define DEBUG1
Definition elog.h:31
#define ereport(elevel,...)
Definition elog.h:152
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:548
static int fb(int x)
void procsignal_sigusr1_handler(SIGNAL_ARGS)
Definition procsignal.c:696
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 579 of file stashpersist.c.

580{
581 for (const char *p = str; *p != '\0'; p++)
582 {
583 switch (*p)
584 {
585 case '\\':
587 break;
588 case '\t':
590 break;
591 case '\n':
593 break;
594 case '\r':
596 break;
597 default:
599 break;
600 }
601 }
602}
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 611 of file stashpersist.c.

612{
613 char *start = *cursor;
614 char *p = start;
615
616 if (*p == '\0')
617 return NULL;
618
619 while (*p != '\0' && *p != '\t')
620 p++;
621
622 if (*p == '\t')
623 *p++ = '\0';
624
625 *cursor = p;
626 return start;
627}
return str start
Definition type.h:139

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 /* Reject overlong stash names. */
378 if (strlen(name) >= NAMEDATALEN)
381 errmsg("syntax error in file \"%s\" line %u: stash name too long",
382 PGSA_DUMP_FILE, lineno)));
383
384 /* Duplicate check. */
386 if (found)
389 errmsg("syntax error in file \"%s\" line %u: duplicate stash name \"%s\"",
390 PGSA_DUMP_FILE, lineno, name)));
391 num_stashes++;
392 }
393 else if (strcmp(line_type, "entry") == 0)
394 {
395 char *stash_name;
396 char *queryid_str;
397 char *advice_str;
398 char *endptr;
399 int64 queryId;
400
401 /* Second field should be the stash name. */
402 stash_name = pgsa_next_tsv_field(&cursor);
403 if (stash_name == NULL)
406 errmsg("syntax error in file \"%s\" line %u: expected stash name",
407 PGSA_DUMP_FILE, lineno)));
408
409 /* Third field should be the query ID. */
411 if (queryid_str == NULL)
414 errmsg("syntax error in file \"%s\" line %u: expected query ID",
415 PGSA_DUMP_FILE, lineno)));
416
417 /* Fourth field should be the advice string. */
419 if (advice_str == NULL)
422 errmsg("syntax error in file \"%s\" line %u: expected advice string",
423 PGSA_DUMP_FILE, lineno)));
424
425 /* No further fields are expected. */
426 if (*cursor != '\0')
429 errmsg("syntax error in file \"%s\" line %u: expected end of line",
430 PGSA_DUMP_FILE, lineno)));
431
432 /* Make sure the stash is one we've actually seen. */
434 stash_name) == NULL)
437 errmsg("syntax error in file \"%s\" line %u: unknown stash \"%s\"",
438 PGSA_DUMP_FILE, lineno, stash_name)));
439
440 /* Parse the query ID. */
441 errno = 0;
442 queryId = strtoi64(queryid_str, &endptr, 10);
443 if (*endptr != '\0' || errno != 0 || queryid_str == endptr ||
444 queryId == 0)
447 errmsg("syntax error in file \"%s\" line %u: invalid query ID \"%s\"",
448 PGSA_DUMP_FILE, lineno, queryid_str)));
449
450 /* Unescape the advice string. */
452
453 /* Append to the entry array. */
454 if (num_entries >= max_entries)
455 {
456 max_entries *= 2;
457 entries = repalloc(entries,
458 max_entries * sizeof(pgsa_saved_entry));
459 }
460 entries[num_entries].stash_name = stash_name;
461 entries[num_entries].queryId = queryId;
462 entries[num_entries].advice_string = advice_str;
463 num_entries++;
464 }
465 else
466 {
469 errmsg("syntax error in file \"%s\" line %u: unrecognized line type",
470 PGSA_DUMP_FILE, lineno)));
471 }
472 }
473
474 /*
475 * Parsing succeeded. Apply everything to shared memory.
476 *
477 * At this point, we know that the file we just read is fully valid, but
478 * it's still possible for this to fail if, for example, DSA memory cannot
479 * be allocated. If that happens, the worker will die, the postmaster will
480 * eventually restart it, and we'll try again after clearing any data that
481 * we did manage to put into shared memory. (Note that we call
482 * pgsa_reset_all_stashes() at the top of this function.)
483 */
485 pgsa_restore_entries(entries, num_entries);
486
487 /* Hooray, it worked! Notify the user. */
488 ereport(LOG,
489 (errmsg("loaded %d advice stashes and %d entries from \"%s\"",
490 num_stashes, num_entries, PGSA_DUMP_FILE)));
491
492 /* Clean up. */
495}
#define Assert(condition)
Definition c.h:943
int64_t int64
Definition c.h:621
int errcode_for_file_access(void)
Definition elog.c:898
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
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:1635
void * palloc(Size size)
Definition mcxt.c:1390
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
void * palloc_extended(Size size, int flags)
Definition mcxt.c:1442
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
#define ERRCODE_DATA_CORRUPTED
#define NAMEDATALEN
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, NAMEDATALEN, 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 633 of file stashpersist.c.

634{
636 for (int i = 0; i < num_entries; i++)
637 {
639 errmsg("restoring advice stash entry for \"%s\", query ID %" PRId64,
640 entries[i].stash_name, entries[i].queryId));
641 pgsa_set_advice_string(entries[i].stash_name,
642 entries[i].queryId,
643 entries[i].advice_string);
644 }
646}
#define DEBUG2
Definition elog.h:30
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 676 of file stashpersist.c.

677{
678 char *src = str;
679 char *dst = str;
680
681 while (*src != '\0')
682 {
683 /* Just pass through anything that's not a backslash-escape. */
684 if (likely(*src != '\\'))
685 {
686 *dst++ = *src++;
687 continue;
688 }
689
690 /* Check what sort of escape we've got. */
691 switch (src[1])
692 {
693 case '\\':
694 *dst++ = '\\';
695 break;
696 case 't':
697 *dst++ = '\t';
698 break;
699 case 'n':
700 *dst++ = '\n';
701 break;
702 case 'r':
703 *dst++ = '\r';
704 break;
705 case '\0':
708 errmsg("syntax error in file \"%s\" line %u: trailing backslash",
709 filename, lineno)));
710 break;
711 default:
714 errmsg("syntax error in file \"%s\" line %u: unrecognized escape \"\\%c\"",
715 filename, lineno, src[1])));
716 break;
717 }
718
719 /* We consumed the backslash and the following character. */
720 src += 2;
721 }
722 *dst = '\0';
723}
#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 729 of file stashpersist.c.

730{
732 pgsa_entry *entry;
733
734 dshash_seq_init(&iter, pgsa_entry_dshash, false);
735 while ((entry = dshash_seq_next(&iter)) != NULL)
736 {
738 char *advice_string;
739
740 if (entry->advice_string == InvalidDsaPointer)
741 continue;
742
744 entry->key.pgsa_stash_id);
745 if (n == NULL)
746 continue; /* orphan entry, skip */
747
748 advice_string = dsa_get_address(pgsa_dsa_area, entry->advice_string);
749
750 resetStringInfo(&wctx->buf);
751 appendStringInfo(&wctx->buf, "entry\t%s\t%" PRId64 "\t",
752 n->name, entry->key.queryId);
753 pgsa_append_tsv_escaped_string(&wctx->buf, advice_string);
754 appendStringInfoChar(&wctx->buf, '\n');
755 fwrite(wctx->buf.data, 1, wctx->buf.len, wctx->file);
756 if (ferror(wctx->file))
758 wctx->entries_written++;
759 }
760 dshash_seq_term(&iter);
761}
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 767 of file stashpersist.c.

768{
769 int save_errno = errno;
770
771 FreeFile(wctx->file);
772 unlink(wctx->pathname);
776 errmsg("could not write to file \"%s\": %m", wctx->pathname)));
777}

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 784 of file stashpersist.c.

785{
788
789 dshash_seq_init(&iter, pgsa_stash_dshash, false);
790 while ((stash = dshash_seq_next(&iter)) != NULL)
791 {
793 bool found;
794
795 n = pgsa_stash_name_table_insert(wctx->nhash, stash->pgsa_stash_id,
796 &found);
797 Assert(!found);
798 n->name = pstrdup(stash->name);
799
800 resetStringInfo(&wctx->buf);
801 appendStringInfo(&wctx->buf, "stash\t%s\n", n->name);
802 fwrite(wctx->buf.data, 1, wctx->buf.len, wctx->file);
803 if (ferror(wctx->file))
805 }
806 dshash_seq_term(&iter);
807}
char * pstrdup(const char *in)
Definition mcxt.c:1910
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 505 of file stashpersist.c.

506{
510
512
513 /* Use a temporary context so all allocations are freed at the end. */
515 "pg_stash_advice dump",
518
519 /* Set up the writer context. */
520 snprintf(wctx.pathname, MAXPGPATH, "%s.tmp", PGSA_DUMP_FILE);
521 wctx.file = AllocateFile(wctx.pathname, "w");
522 if (!wctx.file)
525 errmsg("could not open file \"%s\": %m", wctx.pathname)));
527 initStringInfo(&wctx.buf);
528
529 /* Write stash lines, then entry lines. */
532
533 /*
534 * If nothing was written, remove both the temp file and any existing dump
535 * file rather than installing a zero-length file.
536 */
537 if (wctx.nhash->members == 0)
538 {
540 errmsg("there are no advice stashes to save"));
541 FreeFile(wctx.file);
542 unlink(wctx.pathname);
543 if (unlink(PGSA_DUMP_FILE) == 0)
545 errmsg("removed \"%s\"", PGSA_DUMP_FILE));
546 }
547 else
548 {
549 if (FreeFile(wctx.file) != 0)
550 {
551 int save_errno = errno;
552
553 unlink(wctx.pathname);
557 errmsg("could not close file \"%s\": %m",
558 wctx.pathname)));
559 }
561
562 ereport(LOG,
563 errmsg("saved %d advice stashes and %d entries to \"%s\"",
564 (int) wctx.nhash->members, wctx.entries_written,
566 }
567
570}
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition fd.c:783
#define MAXPGPATH
#define snprintf
Definition port.h:261
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().