PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_waldump.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_waldump.c - decode and display WAL
4 *
5 * Copyright (c) 2013-2026, PostgreSQL Global Development Group
6 *
7 * IDENTIFICATION
8 * src/bin/pg_waldump/pg_waldump.c
9 *-------------------------------------------------------------------------
10 */
11
12#define FRONTEND 1
13#include "postgres.h"
14
15#include <dirent.h>
16#include <limits.h>
17#include <signal.h>
18#include <sys/stat.h>
19#include <unistd.h>
20
21#include "access/transam.h"
23#include "access/xlogreader.h"
24#include "access/xlogrecord.h"
25#include "access/xlogstats.h"
26#include "common/fe_memutils.h"
27#include "common/file_perm.h"
28#include "common/file_utils.h"
29#include "common/logging.h"
30#include "common/relpath.h"
31#include "getopt_long.h"
32#include "pg_waldump.h"
33#include "rmgrdesc.h"
34#include "storage/bufpage.h"
35
36/*
37 * NOTE: For any code change or issue fix here, it is highly recommended to
38 * give a thought about doing the same in pg_walinspect contrib module as well.
39 */
40
41static const char *progname;
42
43static volatile sig_atomic_t time_to_stop = false;
44
45static const RelFileLocator emptyRelFileLocator = {0, 0, 0};
46
74
75
76/*
77 * When sigint is called, just tell the system to exit at the next possible
78 * moment.
79 */
80#ifndef WIN32
81
82static void
87#endif
88
89static void
91{
92 int i;
93
94 for (i = 0; i <= RM_MAX_BUILTIN_ID; i++)
95 {
96 printf("%s\n", GetRmgrDesc(i)->rm_name);
97 }
98}
99
100/*
101 * Check whether directory exists and whether we can open it. Keep errno set so
102 * that the caller can report errors somewhat more accurately.
103 */
104static bool
106{
107 DIR *dir = opendir(directory);
108
109 if (dir == NULL)
110 return false;
111 closedir(dir);
112 return true;
113}
114
115/*
116 * Create if necessary the directory storing the full-page images extracted
117 * from the WAL records read.
118 */
119static void
121{
122 int ret;
123
124 switch ((ret = pg_check_dir(path)))
125 {
126 case 0:
127 /* Does not exist, so create it */
128 if (pg_mkdir_p(path, pg_dir_create_mode) < 0)
129 pg_fatal("could not create directory \"%s\": %m", path);
130 break;
131 case 1:
132 /* Present and empty, so do nothing */
133 break;
134 case 2:
135 case 3:
136 case 4:
137 /* Exists and not empty */
138 pg_fatal("directory \"%s\" exists but is not empty", path);
139 break;
140 default:
141 /* Trouble accessing directory */
142 pg_fatal("could not access directory \"%s\": %m", path);
143 }
144}
145
146/*
147 * Split a pathname as dirname(1) and basename(1) would.
148 *
149 * XXX this probably doesn't do very well on Windows. We probably need to
150 * apply canonicalize_path(), at the very least.
151 */
152static void
153split_path(const char *path, char **dir, char **fname)
154{
155 const char *sep;
156
157 /* split filepath into directory & filename */
158 sep = strrchr(path, '/');
159
160 /* directory path */
161 if (sep != NULL)
162 {
163 *dir = pnstrdup(path, sep - path);
164 *fname = pg_strdup(sep + 1);
165 }
166 /* local directory */
167 else
168 {
169 *dir = NULL;
170 *fname = pg_strdup(path);
171 }
172}
173
174/*
175 * Open the file in the valid target directory.
176 *
177 * return a read only fd
178 */
179int
180open_file_in_directory(const char *directory, const char *fname)
181{
182 int fd = -1;
183 char fpath[MAXPGPATH];
184
186
187 snprintf(fpath, MAXPGPATH, "%s/%s", directory, fname);
188 fd = open(fpath, O_RDONLY | PG_BINARY, 0);
189
190 if (fd < 0 && errno != ENOENT)
191 pg_fatal("could not open file \"%s\": %m", fname);
192 return fd;
193}
194
195/*
196 * Try to find fname in the given directory. Returns true if it is found,
197 * false otherwise. If fname is NULL, search the complete directory for any
198 * file with a valid WAL file name. If file is successfully opened, set
199 * *WaSegSz to the WAL segment size.
200 */
201static bool
202search_directory(const char *directory, const char *fname, int *WalSegSz)
203{
204 int fd = -1;
205 DIR *xldir;
206
207 /* open file if valid filename is provided */
208 if (fname != NULL)
210
211 /*
212 * A valid file name is not passed, so search the complete directory. If
213 * we find any file whose name is a valid WAL file name then try to open
214 * it. If we cannot open it, bail out.
215 */
216 else if ((xldir = opendir(directory)) != NULL)
217 {
218 struct dirent *xlde;
219
220 while ((xlde = readdir(xldir)) != NULL)
221 {
222 if (IsXLogFileName(xlde->d_name))
223 {
225 fname = pg_strdup(xlde->d_name);
226 break;
227 }
228 }
229
231 }
232
233 /* set WalSegSz if file is successfully opened */
234 if (fd >= 0)
235 {
237 int r;
238
239 r = read(fd, buf.data, XLOG_BLCKSZ);
240 if (r == XLOG_BLCKSZ)
241 {
243
244 if (!IsValidWalSegSize(longhdr->xlp_seg_size))
245 {
246 pg_log_error(ngettext("invalid WAL segment size in WAL file \"%s\" (%d byte)",
247 "invalid WAL segment size in WAL file \"%s\" (%d bytes)",
248 longhdr->xlp_seg_size),
249 fname, longhdr->xlp_seg_size);
250 pg_log_error_detail("The WAL segment size must be a power of two between 1 MB and 1 GB.");
251 exit(1);
252 }
253
254 *WalSegSz = longhdr->xlp_seg_size;
255 }
256 else if (r < 0)
257 pg_fatal("could not read file \"%s\": %m",
258 fname);
259 else
260 pg_fatal("could not read file \"%s\": read %d of %d",
261 fname, r, XLOG_BLCKSZ);
262 close(fd);
263 return true;
264 }
265
266 return false;
267}
268
269/*
270 * Identify the target directory.
271 *
272 * Try to find the file in several places:
273 * if directory != NULL:
274 * directory /
275 * directory / XLOGDIR /
276 * else
277 * .
278 * XLOGDIR /
279 * $PGDATA / XLOGDIR /
280 *
281 * The valid target directory is returned, and *WalSegSz is set to the
282 * size of the WAL segment found in that directory.
283 */
284static char *
286{
287 char fpath[MAXPGPATH];
288
289 if (directory != NULL)
290 {
292 return pg_strdup(directory);
293
294 /* directory / XLOGDIR */
296 if (search_directory(fpath, fname, WalSegSz))
297 return pg_strdup(fpath);
298 }
299 else
300 {
301 const char *datadir;
302
303 /* current directory */
304 if (search_directory(".", fname, WalSegSz))
305 return pg_strdup(".");
306 /* XLOGDIR */
307 if (search_directory(XLOGDIR, fname, WalSegSz))
308 return pg_strdup(XLOGDIR);
309
310 datadir = getenv("PGDATA");
311 /* $PGDATA / XLOGDIR */
312 if (datadir != NULL)
313 {
315 if (search_directory(fpath, fname, WalSegSz))
316 return pg_strdup(fpath);
317 }
318 }
319
320 /* could not locate WAL file */
321 if (fname)
322 pg_fatal("could not locate WAL file \"%s\"", fname);
323 else
324 pg_fatal("could not find any WAL file");
325
326 return NULL; /* not reached */
327}
328
329/*
330 * Returns the number of bytes to read for the given page. Returns -1 if
331 * the requested range has already been reached or exceeded.
332 */
333static inline int
335 int reqLen)
336{
337 int count = XLOG_BLCKSZ;
338
339 if (XLogRecPtrIsValid(private->endptr))
340 {
342 count = XLOG_BLCKSZ;
343 else if (targetPagePtr + reqLen <= private->endptr)
344 count = private->endptr - targetPagePtr;
345 else
346 {
347 private->endptr_reached = true;
348 return -1;
349 }
350 }
351
352 return count;
353}
354
355/* pg_waldump's XLogReaderRoutine->segment_open callback */
356static void
359{
360 TimeLineID tli = *tli_p;
361 char fname[MAXPGPATH];
362 int tries;
363
364 XLogFileName(fname, tli, nextSegNo, state->segcxt.ws_segsize);
365
366 /*
367 * In follow mode there is a short period of time after the server has
368 * written the end of the previous file before the new file is available.
369 * So we loop for 5 seconds looking for the file to appear before giving
370 * up.
371 */
372 for (tries = 0; tries < 10; tries++)
373 {
374 state->seg.ws_file = open_file_in_directory(state->segcxt.ws_dir, fname);
375 if (state->seg.ws_file >= 0)
376 return;
377 if (errno == ENOENT)
378 {
379 int save_errno = errno;
380
381 /* File not there yet, try again */
382 pg_usleep(500 * 1000);
383
385 continue;
386 }
387 /* Any other error, fall through and fail */
388 break;
389 }
390
391 pg_fatal("could not find file \"%s\": %m", fname);
392}
393
394/*
395 * pg_waldump's XLogReaderRoutine->segment_close callback. Same as
396 * wal_segment_close
397 */
398static void
400{
401 close(state->seg.ws_file);
402 /* need to check errno? */
403 state->seg.ws_file = -1;
404}
405
406/* pg_waldump's XLogReaderRoutine->page_read callback */
407static int
410{
411 XLogDumpPrivate *private = state->private_data;
412 int count = required_read_len(private, targetPagePtr, reqLen);
414
415 /* Bail out if the end of the requested range has already been reached */
416 if (count < 0)
417 return -1;
418
419 if (!WALRead(state, readBuff, targetPagePtr, count, private->timeline,
420 &errinfo))
421 {
422 WALOpenSegment *seg = &errinfo.wre_seg;
423 char fname[MAXPGPATH];
424
425 XLogFileName(fname, seg->ws_tli, seg->ws_segno,
426 state->segcxt.ws_segsize);
427
428 if (errinfo.wre_errno != 0)
429 {
430 errno = errinfo.wre_errno;
431 pg_fatal("could not read from file \"%s\", offset %d: %m",
432 fname, errinfo.wre_off);
433 }
434 else
435 pg_fatal("could not read from file \"%s\", offset %d: read %d of %d",
436 fname, errinfo.wre_off, errinfo.wre_read,
437 errinfo.wre_req);
438 }
439
440 return count;
441}
442
443/*
444 * pg_waldump's XLogReaderRoutine->segment_open callback to support dumping WAL
445 * files from tar archives. Segment tracking is handled by
446 * TarWALDumpReadPage, so no action is needed here.
447 */
448static void
451{
452 /* No action needed */
453}
454
455/*
456 * pg_waldump's XLogReaderRoutine->segment_close callback to support dumping
457 * WAL files from tar archives. Segment tracking is handled by
458 * TarWALDumpReadPage, so no action is needed here.
459 */
460static void
462{
463 /* No action needed */
464}
465
466/*
467 * pg_waldump's XLogReaderRoutine->page_read callback to support dumping WAL
468 * files from tar archives.
469 */
470static int
473{
474 XLogDumpPrivate *private = state->private_data;
475 int count = required_read_len(private, targetPagePtr, reqLen);
476 int segsize = state->segcxt.ws_segsize;
478
479 /* Bail out if the end of the requested range has already been reached */
480 if (count < 0)
481 return -1;
482
483 /*
484 * If the target page is in a different segment, release the hash entry
485 * buffer and remove any spilled temporary file for the previous segment.
486 * Since pg_waldump never requests the same WAL bytes twice, moving to a
487 * new segment means the previous segment's data will not be needed again.
488 *
489 * Afterward, check whether the next required WAL segment was already
490 * spilled to the temporary directory before invoking the archive
491 * streamer.
492 */
493 curSegNo = state->seg.ws_segno;
494 if (!XLByteInSeg(targetPagePtr, curSegNo, segsize))
495 {
496 char fname[MAXFNAMELEN];
498
499 /*
500 * Calculate the next WAL segment to be decoded from the given page
501 * pointer.
502 */
504 state->seg.ws_tli = private->timeline;
505 state->seg.ws_segno = nextSegNo;
506
507 /* Close the WAL segment file if it is currently open */
508 if (state->seg.ws_file >= 0)
509 {
510 close(state->seg.ws_file);
511 state->seg.ws_file = -1;
512 }
513
514 /*
515 * If in pre-reading mode (prior to actual decoding), do not delete
516 * any entries that might be requested again once the decoding loop
517 * starts. For more details, see the comments in
518 * read_archive_wal_page().
519 */
520 if (private->decoding_started && curSegNo < nextSegNo)
521 {
522 XLogFileName(fname, state->seg.ws_tli, curSegNo, segsize);
523 free_archive_wal_entry(fname, private);
524 }
525
526 /*
527 * If the next segment exists in the temporary spill directory, open
528 * it and continue reading from there.
529 */
530 if (TmpWalSegDir != NULL)
531 {
532 XLogFileName(fname, state->seg.ws_tli, nextSegNo, segsize);
533 state->seg.ws_file = open_file_in_directory(TmpWalSegDir, fname);
534 }
535 }
536
537 /* Continue reading from the open WAL segment, if any */
538 if (state->seg.ws_file >= 0)
540 readBuff);
541
542 /* Otherwise, read the WAL page from the archive streamer */
543 return read_archive_wal_page(private, targetPagePtr, count, readBuff);
544}
545
546/*
547 * Boolean to return whether the given WAL record matches a specific relation
548 * and optionally block.
549 */
550static bool
555{
556 int block_id;
557
558 for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
559 {
560 RelFileLocator rlocator;
561 ForkNumber forknum;
563
565 &rlocator, &forknum, &blk, NULL))
566 continue;
567
568 if ((matchFork == InvalidForkNumber || matchFork == forknum) &&
572 return true;
573 }
574
575 return false;
576}
577
578/*
579 * Boolean to return whether the given WAL record contains a full page write.
580 */
581static bool
583{
584 int block_id;
585
586 for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
587 {
588 if (!XLogRecHasBlockRef(record, block_id))
589 continue;
590
591 if (XLogRecHasBlockImage(record, block_id))
592 return true;
593 }
594
595 return false;
596}
597
598/*
599 * Function to externally save all FPWs stored in the given WAL record.
600 * Decompression is applied to all the blocks saved, if necessary.
601 */
602static void
604{
605 int block_id;
606
607 for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
608 {
610 Page page;
611 char filename[MAXPGPATH];
612 char forkname[FORKNAMECHARS + 2]; /* _ + terminating zero */
613 FILE *file;
617
618 if (!XLogRecHasBlockRef(record, block_id))
619 continue;
620
621 if (!XLogRecHasBlockImage(record, block_id))
622 continue;
623
624 page = (Page) buf.data;
625
626 /* Full page exists, so let's save it */
627 if (!RestoreBlockImage(record, block_id, page))
628 pg_fatal("%s", record->errormsg_buf);
629
631 &rnode, &fork, &blk, NULL);
632
633 if (fork >= 0 && fork <= MAX_FORKNUM)
634 sprintf(forkname, "_%s", forkNames[fork]);
635 else
636 pg_fatal("invalid fork number: %u", fork);
637
638 snprintf(filename, MAXPGPATH, "%s/%08X-%08X-%08X.%u.%u.%u.%u%s", savepath,
639 record->seg.ws_tli,
641 rnode.spcOid, rnode.dbOid, rnode.relNumber, blk, forkname);
642
643 file = fopen(filename, PG_BINARY_W);
644 if (!file)
645 pg_fatal("could not open file \"%s\": %m", filename);
646
647 if (fwrite(page, BLCKSZ, 1, file) != 1)
648 pg_fatal("could not write file \"%s\": %m", filename);
649
650 if (fclose(file) != 0)
651 pg_fatal("could not close file \"%s\": %m", filename);
652 }
653}
654
655/*
656 * Print a record to stdout
657 */
658static void
660{
661 const char *id;
662 const RmgrDescData *desc = GetRmgrDesc(XLogRecGetRmid(record));
663 uint32 rec_len;
664 uint32 fpi_len;
665 uint8 info = XLogRecGetInfo(record);
666 XLogRecPtr xl_prev = XLogRecGetPrev(record);
668
669 XLogRecGetLen(record, &rec_len, &fpi_len);
670
671 printf("rmgr: %-11s len (rec/tot): %6u/%6u, tx: %10u, lsn: %X/%08X, prev %X/%08X, ",
672 desc->rm_name,
673 rec_len, XLogRecGetTotalLen(record),
674 XLogRecGetXid(record),
676 LSN_FORMAT_ARGS(xl_prev));
677
678 id = desc->rm_identify(info);
679 if (id == NULL)
680 printf("desc: UNKNOWN (%x) ", info & ~XLR_INFO_MASK);
681 else
682 printf("desc: %s ", id);
683
684 initStringInfo(&s);
685 desc->rm_desc(&s, record);
686 printf("%s", s.data);
687
688 resetStringInfo(&s);
689 XLogRecGetBlockRefInfo(record, true, config->bkp_details, &s, NULL);
690 printf("%s", s.data);
691 pfree(s.data);
692}
693
694/*
695 * Display a single row of record counts and sizes for an rmgr or record.
696 */
697static void
700 uint64 rec_len, uint64 total_rec_len,
701 uint64 fpi_len, uint64 total_fpi_len,
702 uint64 tot_len, uint64 total_len)
703{
704 double n_pct,
708
709 n_pct = 0;
710 if (total_count != 0)
711 n_pct = 100 * (double) n / total_count;
712
713 rec_len_pct = 0;
714 if (total_rec_len != 0)
715 rec_len_pct = 100 * (double) rec_len / total_rec_len;
716
717 fpi_len_pct = 0;
718 if (total_fpi_len != 0)
719 fpi_len_pct = 100 * (double) fpi_len / total_fpi_len;
720
721 tot_len_pct = 0;
722 if (total_len != 0)
723 tot_len_pct = 100 * (double) tot_len / total_len;
724
725 printf("%-27s "
726 "%20" PRIu64 " (%6.02f) "
727 "%20" PRIu64 " (%6.02f) "
728 "%20" PRIu64 " (%6.02f) "
729 "%20" PRIu64 " (%6.02f)\n",
730 name, n, n_pct, rec_len, rec_len_pct, fpi_len, fpi_len_pct,
732}
733
734
735/*
736 * Display summary statistics about the records seen so far.
737 */
738static void
740{
741 int ri,
742 rj;
746 uint64 total_len = 0;
747 double rec_len_pct,
749
750 /*
751 * Leave if no stats have been computed yet, as tracked by the end LSN.
752 */
753 if (!XLogRecPtrIsValid(stats->endptr))
754 return;
755
756 /*
757 * Each row shows its percentages of the total, so make a first pass to
758 * calculate column totals.
759 */
760
761 for (ri = 0; ri <= RM_MAX_ID; ri++)
762 {
763 if (!RmgrIdIsValid(ri))
764 continue;
765
766 total_count += stats->rmgr_stats[ri].count;
769 }
770 total_len = total_rec_len + total_fpi_len;
771
772 printf("WAL statistics between %X/%08X and %X/%08X:\n",
773 LSN_FORMAT_ARGS(stats->startptr), LSN_FORMAT_ARGS(stats->endptr));
774
775 /*
776 * 27 is strlen("Transaction/COMMIT_PREPARED"), 20 is strlen(2^64), 8 is
777 * strlen("(100.00%)")
778 */
779
780 printf("%-27s %20s %8s %20s %8s %20s %8s %20s %8s\n"
781 "%-27s %20s %8s %20s %8s %20s %8s %20s %8s\n",
782 "Type", "N", "(%)", "Record size", "(%)", "FPI size", "(%)", "Combined size", "(%)",
783 "----", "-", "---", "-----------", "---", "--------", "---", "-------------", "---");
784
785 for (ri = 0; ri <= RM_MAX_ID; ri++)
786 {
787 uint64 count,
788 rec_len,
789 fpi_len,
790 tot_len;
791 const RmgrDescData *desc;
792
793 if (!RmgrIdIsValid(ri))
794 continue;
795
796 desc = GetRmgrDesc(ri);
797
798 if (!config->stats_per_record)
799 {
800 count = stats->rmgr_stats[ri].count;
801 rec_len = stats->rmgr_stats[ri].rec_len;
802 fpi_len = stats->rmgr_stats[ri].fpi_len;
803 tot_len = rec_len + fpi_len;
804
805 if (RmgrIdIsCustom(ri) && count == 0)
806 continue;
807
809 count, total_count, rec_len, total_rec_len,
810 fpi_len, total_fpi_len, tot_len, total_len);
811 }
812 else
813 {
814 for (rj = 0; rj < MAX_XLINFO_TYPES; rj++)
815 {
816 const char *id;
817
818 count = stats->record_stats[ri][rj].count;
819 rec_len = stats->record_stats[ri][rj].rec_len;
820 fpi_len = stats->record_stats[ri][rj].fpi_len;
821 tot_len = rec_len + fpi_len;
822
823 /* Skip undefined combinations and ones that didn't occur */
824 if (count == 0)
825 continue;
826
827 /* the upper four bits in xl_info are the rmgr's */
828 id = desc->rm_identify(rj << 4);
829 if (id == NULL)
830 id = psprintf("UNKNOWN (%x)", rj << 4);
831
832 XLogDumpStatsRow(psprintf("%s/%s", desc->rm_name, id),
833 count, total_count, rec_len, total_rec_len,
834 fpi_len, total_fpi_len, tot_len, total_len);
835 }
836 }
837 }
838
839 printf("%-27s %20s %8s %20s %8s %20s %8s %20s\n",
840 "", "--------", "", "--------", "", "--------", "", "--------");
841
842 /*
843 * The percentages in earlier rows were calculated against the column
844 * total, but the ones that follow are against the row total. Note that
845 * these are displayed with a % symbol to differentiate them from the
846 * earlier ones, and are thus up to 9 characters long.
847 */
848
849 rec_len_pct = 0;
850 if (total_len != 0)
851 rec_len_pct = 100 * (double) total_rec_len / total_len;
852
853 fpi_len_pct = 0;
854 if (total_len != 0)
855 fpi_len_pct = 100 * (double) total_fpi_len / total_len;
856
857 printf("%-27s "
858 "%20" PRIu64 " %-9s"
859 "%20" PRIu64 " %-9s"
860 "%20" PRIu64 " %-9s"
861 "%20" PRIu64 " %-6s\n",
862 "Total", stats->count, "",
863 total_rec_len, psprintf("[%.02f%%]", rec_len_pct),
864 total_fpi_len, psprintf("[%.02f%%]", fpi_len_pct),
865 total_len, "[100%]");
866}
867
868static void
869usage(void)
870{
871 printf(_("%s decodes and displays PostgreSQL write-ahead logs for debugging.\n\n"),
872 progname);
873 printf(_("Usage:\n"));
874 printf(_(" %s [OPTION]... [STARTSEG [ENDSEG]]\n"), progname);
875 printf(_("\nOptions:\n"));
876 printf(_(" -b, --bkp-details output detailed information about backup blocks\n"));
877 printf(_(" -B, --block=N with --relation, only show records that modify block N\n"));
878 printf(_(" -e, --end=RECPTR stop reading at WAL location RECPTR\n"));
879 printf(_(" -f, --follow keep retrying after reaching end of WAL\n"));
880 printf(_(" -F, --fork=FORK only show records that modify blocks in fork FORK;\n"
881 " valid names are main, fsm, vm, init\n"));
882 printf(_(" -n, --limit=N number of records to display\n"));
883 printf(_(" -p, --path=PATH a tar archive or a directory in which to find WAL segment files or\n"
884 " a directory with a pg_wal subdirectory containing such files\n"
885 " (default: current directory, ./pg_wal, $PGDATA/pg_wal)\n"));
886 printf(_(" -q, --quiet do not print any output, except for errors\n"));
887 printf(_(" -r, --rmgr=RMGR only show records generated by resource manager RMGR;\n"
888 " use --rmgr=list to list valid resource manager names\n"));
889 printf(_(" -R, --relation=T/D/R only show records that modify blocks in relation T/D/R\n"));
890 printf(_(" -s, --start=RECPTR start reading at WAL location RECPTR\n"));
891 printf(_(" -t, --timeline=TLI timeline from which to read WAL records\n"
892 " (default: 1 or the value used in STARTSEG)\n"));
893 printf(_(" -V, --version output version information, then exit\n"));
894 printf(_(" -w, --fullpage only show records with a full page write\n"));
895 printf(_(" -x, --xid=XID only show records with transaction ID XID\n"));
896 printf(_(" -z, --stats[=record] show statistics instead of records\n"
897 " (optionally, show per-record statistics)\n"));
898 printf(_(" --save-fullpage=DIR save full page images to DIR\n"));
899 printf(_(" -?, --help show this help, then exit\n"));
900 printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
901 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
902}
903
904int
905main(int argc, char **argv)
906{
910 XLogDumpPrivate private;
911 XLogDumpConfig config;
912 XLogStats stats;
913 XLogRecord *record;
915 char *waldir = NULL;
916 char *errormsg;
918
919 static struct option long_options[] = {
920 {"bkp-details", no_argument, NULL, 'b'},
921 {"block", required_argument, NULL, 'B'},
922 {"end", required_argument, NULL, 'e'},
923 {"follow", no_argument, NULL, 'f'},
924 {"fork", required_argument, NULL, 'F'},
925 {"fullpage", no_argument, NULL, 'w'},
926 {"help", no_argument, NULL, '?'},
927 {"limit", required_argument, NULL, 'n'},
928 {"path", required_argument, NULL, 'p'},
929 {"quiet", no_argument, NULL, 'q'},
930 {"relation", required_argument, NULL, 'R'},
931 {"rmgr", required_argument, NULL, 'r'},
932 {"start", required_argument, NULL, 's'},
933 {"timeline", required_argument, NULL, 't'},
934 {"xid", required_argument, NULL, 'x'},
935 {"version", no_argument, NULL, 'V'},
936 {"stats", optional_argument, NULL, 'z'},
937 {"save-fullpage", required_argument, NULL, 1},
938 {NULL, 0, NULL, 0}
939 };
940
941 int option;
942 int optindex = 0;
943
944#ifndef WIN32
946#endif
947
948 pg_logging_init(argv[0]);
949 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_waldump"));
950 progname = get_progname(argv[0]);
951
952 if (argc > 1)
953 {
954 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
955 {
956 usage();
957 exit(0);
958 }
959 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
960 {
961 puts("pg_waldump (PostgreSQL) " PG_VERSION);
962 exit(0);
963 }
964 }
965
966 memset(&private, 0, sizeof(XLogDumpPrivate));
967 memset(&config, 0, sizeof(XLogDumpConfig));
968 memset(&stats, 0, sizeof(XLogStats));
969
970 private.timeline = 1;
971 private.segsize = 0;
972 private.startptr = InvalidXLogRecPtr;
973 private.endptr = InvalidXLogRecPtr;
974 private.endptr_reached = false;
975 private.decoding_started = false;
976 private.archive_name = NULL;
977 private.start_segno = 0;
978 private.end_segno = UINT64_MAX;
979
980 config.quiet = false;
981 config.bkp_details = false;
982 config.stop_after_records = -1;
983 config.already_displayed_records = 0;
984 config.follow = false;
985 /* filter_by_rmgr array was zeroed by memset above */
986 config.filter_by_rmgr_enabled = false;
988 config.filter_by_xid_enabled = false;
989 config.filter_by_extended = false;
990 config.filter_by_relation_enabled = false;
993 config.filter_by_fpw = false;
994 config.save_fullpage_path = NULL;
995 config.stats = false;
996 config.stats_per_record = false;
997
998 stats.startptr = InvalidXLogRecPtr;
999 stats.endptr = InvalidXLogRecPtr;
1000
1001 if (argc <= 1)
1002 {
1003 pg_log_error("no arguments specified");
1004 goto bad_argument;
1005 }
1006
1007 while ((option = getopt_long(argc, argv, "bB:e:fF:n:p:qr:R:s:t:wx:z",
1008 long_options, &optindex)) != -1)
1009 {
1010 switch (option)
1011 {
1012 case 'b':
1013 config.bkp_details = true;
1014 break;
1015 case 'B':
1016 if (sscanf(optarg, "%u", &config.filter_by_relation_block) != 1 ||
1018 {
1019 pg_log_error("invalid block number: \"%s\"", optarg);
1020 goto bad_argument;
1021 }
1023 config.filter_by_extended = true;
1024 break;
1025 case 'e':
1026 if (sscanf(optarg, "%X/%08X", &xlogid, &xrecoff) != 2)
1027 {
1028 pg_log_error("invalid WAL location: \"%s\"",
1029 optarg);
1030 goto bad_argument;
1031 }
1032 private.endptr = (uint64) xlogid << 32 | xrecoff;
1033 break;
1034 case 'f':
1035 config.follow = true;
1036 break;
1037 case 'F':
1040 {
1041 pg_log_error("invalid fork name: \"%s\"", optarg);
1042 goto bad_argument;
1043 }
1044 config.filter_by_extended = true;
1045 break;
1046 case 'n':
1047 if (sscanf(optarg, "%d", &config.stop_after_records) != 1)
1048 {
1049 pg_log_error("invalid value \"%s\" for option %s", optarg, "-n/--limit");
1050 goto bad_argument;
1051 }
1052 break;
1053 case 'p':
1055 break;
1056 case 'q':
1057 config.quiet = true;
1058 break;
1059 case 'r':
1060 {
1061 int rmid;
1062
1063 if (pg_strcasecmp(optarg, "list") == 0)
1064 {
1067 }
1068
1069 /*
1070 * First look for the generated name of a custom rmgr, of
1071 * the form "custom###". We accept this form, because the
1072 * custom rmgr module is not loaded, so there's no way to
1073 * know the real name. This convention should be
1074 * consistent with that in rmgrdesc.c.
1075 */
1076 if (sscanf(optarg, "custom%03d", &rmid) == 1)
1077 {
1078 if (!RmgrIdIsCustom(rmid))
1079 {
1080 pg_log_error("custom resource manager \"%s\" does not exist",
1081 optarg);
1082 goto bad_argument;
1083 }
1084 config.filter_by_rmgr[rmid] = true;
1085 config.filter_by_rmgr_enabled = true;
1086 }
1087 else
1088 {
1089 /* then look for builtin rmgrs */
1090 for (rmid = 0; rmid <= RM_MAX_BUILTIN_ID; rmid++)
1091 {
1092 if (pg_strcasecmp(optarg, GetRmgrDesc(rmid)->rm_name) == 0)
1093 {
1094 config.filter_by_rmgr[rmid] = true;
1095 config.filter_by_rmgr_enabled = true;
1096 break;
1097 }
1098 }
1099 if (rmid > RM_MAX_BUILTIN_ID)
1100 {
1101 pg_log_error("resource manager \"%s\" does not exist",
1102 optarg);
1103 goto bad_argument;
1104 }
1105 }
1106 }
1107 break;
1108 case 'R':
1109 if (sscanf(optarg, "%u/%u/%u",
1110 &config.filter_by_relation.spcOid,
1111 &config.filter_by_relation.dbOid,
1112 &config.filter_by_relation.relNumber) != 3 ||
1115 {
1116 pg_log_error("invalid relation specification: \"%s\"", optarg);
1117 pg_log_error_detail("Expecting \"tablespace OID/database OID/relation filenode\".");
1118 goto bad_argument;
1119 }
1120 config.filter_by_relation_enabled = true;
1121 config.filter_by_extended = true;
1122 break;
1123 case 's':
1124 if (sscanf(optarg, "%X/%08X", &xlogid, &xrecoff) != 2)
1125 {
1126 pg_log_error("invalid WAL location: \"%s\"",
1127 optarg);
1128 goto bad_argument;
1129 }
1130 else
1131 private.startptr = (uint64) xlogid << 32 | xrecoff;
1132 break;
1133 case 't':
1134
1135 /*
1136 * This is like option_parse_int() but needs to handle
1137 * unsigned 32-bit int. Also, we accept both decimal and
1138 * hexadecimal specifications here.
1139 */
1140 {
1141 char *endptr;
1142 unsigned long val;
1143
1144 errno = 0;
1145 val = strtoul(optarg, &endptr, 0);
1146
1147 while (*endptr != '\0' && isspace((unsigned char) *endptr))
1148 endptr++;
1149
1150 if (*endptr != '\0')
1151 {
1152 pg_log_error("invalid value \"%s\" for option %s",
1153 optarg, "-t/--timeline");
1154 goto bad_argument;
1155 }
1156
1158 {
1159 pg_log_error("%s must be in range %u..%u",
1160 "-t/--timeline", 1, UINT_MAX);
1161 goto bad_argument;
1162 }
1163
1164 private.timeline = val;
1165
1166 break;
1167 }
1168 case 'w':
1169 config.filter_by_fpw = true;
1170 break;
1171 case 'x':
1172 if (sscanf(optarg, "%u", &config.filter_by_xid) != 1)
1173 {
1174 pg_log_error("invalid transaction ID specification: \"%s\"",
1175 optarg);
1176 goto bad_argument;
1177 }
1178 config.filter_by_xid_enabled = true;
1179 break;
1180 case 'z':
1181 config.stats = true;
1182 config.stats_per_record = false;
1183 if (optarg)
1184 {
1185 if (strcmp(optarg, "record") == 0)
1186 config.stats_per_record = true;
1187 else if (strcmp(optarg, "rmgr") != 0)
1188 {
1189 pg_log_error("unrecognized value for option %s: %s",
1190 "--stats", optarg);
1191 goto bad_argument;
1192 }
1193 }
1194 break;
1195 case 1:
1197 break;
1198 default:
1199 goto bad_argument;
1200 }
1201 }
1202
1205 {
1206 pg_log_error("option %s requires option %s to be specified",
1207 "-B/--block", "-R/--relation");
1208 goto bad_argument;
1209 }
1210
1211 if ((optind + 2) < argc)
1212 {
1213 pg_log_error("too many command-line arguments (first is \"%s\")",
1214 argv[optind + 2]);
1215 goto bad_argument;
1216 }
1217
1218 if (waldir != NULL)
1219 {
1220 /* Check whether the path looks like a tar archive by its extension */
1221 if (parse_tar_compress_algorithm(waldir, &compression))
1222 {
1223 split_path(waldir, &private.archive_dir, &private.archive_name);
1224 }
1225 /* Otherwise it must be a directory */
1226 else if (!verify_directory(waldir))
1227 {
1228 pg_log_error("could not open directory \"%s\": %m", waldir);
1229 goto bad_argument;
1230 }
1231 }
1232
1233 if (config.save_fullpage_path != NULL)
1235
1236 /* parse files as start/end boundaries, extract path if not specified */
1237 if (optind < argc)
1238 {
1239 char *directory = NULL;
1240 char *fname = NULL;
1241 int fd;
1242 XLogSegNo segno;
1243
1244 /*
1245 * If a tar archive is passed using the --path option, all other
1246 * arguments become unnecessary.
1247 */
1248 if (private.archive_name)
1249 {
1250 pg_log_error("unnecessary command-line arguments specified with tar archive (first is \"%s\")",
1251 argv[optind]);
1252 goto bad_argument;
1253 }
1254
1255 split_path(argv[optind], &directory, &fname);
1256
1257 if (waldir == NULL && directory != NULL)
1258 {
1259 waldir = directory;
1260
1262 pg_fatal("could not open directory \"%s\": %m", waldir);
1263 }
1264
1265 if (fname != NULL && parse_tar_compress_algorithm(fname, &compression))
1266 {
1267 private.archive_dir = waldir;
1268 private.archive_name = fname;
1269 }
1270 else
1271 {
1272 waldir = identify_target_directory(waldir, fname, &private.segsize);
1274 if (fd < 0)
1275 pg_fatal("could not open file \"%s\"", fname);
1276 close(fd);
1277
1278 /* parse position from file */
1279 XLogFromFileName(fname, &private.timeline, &segno, private.segsize);
1280
1281 if (!XLogRecPtrIsValid(private.startptr))
1282 XLogSegNoOffsetToRecPtr(segno, 0, private.segsize, private.startptr);
1283 else if (!XLByteInSeg(private.startptr, segno, private.segsize))
1284 {
1285 pg_log_error("start WAL location %X/%08X is not inside file \"%s\"",
1286 LSN_FORMAT_ARGS(private.startptr),
1287 fname);
1288 goto bad_argument;
1289 }
1290
1291 /* no second file specified, set end position */
1292 if (!(optind + 1 < argc) && !XLogRecPtrIsValid(private.endptr))
1293 XLogSegNoOffsetToRecPtr(segno + 1, 0, private.segsize, private.endptr);
1294
1295 /* parse ENDSEG if passed */
1296 if (optind + 1 < argc)
1297 {
1299
1300 /* ignore directory, already have that */
1301 split_path(argv[optind + 1], &directory, &fname);
1302
1304 if (fd < 0)
1305 pg_fatal("could not open file \"%s\"", fname);
1306 close(fd);
1307
1308 /* parse position from file */
1309 XLogFromFileName(fname, &private.timeline, &endsegno, private.segsize);
1310
1311 if (endsegno < segno)
1312 pg_fatal("ENDSEG %s is before STARTSEG %s",
1313 argv[optind + 1], argv[optind]);
1314
1315 if (!XLogRecPtrIsValid(private.endptr))
1316 XLogSegNoOffsetToRecPtr(endsegno + 1, 0, private.segsize,
1317 private.endptr);
1318
1319 /* set segno to endsegno for check of --end */
1320 segno = endsegno;
1321 }
1322
1323 if (!XLByteInSeg(private.endptr, segno, private.segsize) &&
1324 private.endptr != (segno + 1) * private.segsize)
1325 {
1326 pg_log_error("end WAL location %X/%08X is not inside file \"%s\"",
1327 LSN_FORMAT_ARGS(private.endptr),
1328 argv[argc - 1]);
1329 goto bad_argument;
1330 }
1331 }
1332 }
1333 else if (!private.archive_name)
1334 waldir = identify_target_directory(waldir, NULL, &private.segsize);
1335
1336 /* we don't know what to print */
1337 if (!XLogRecPtrIsValid(private.startptr))
1338 {
1339 pg_log_error("no start WAL location given");
1340 goto bad_argument;
1341 }
1342
1343 /* --follow is not supported with tar archives */
1344 if (config.follow && private.archive_name)
1345 {
1346 pg_log_error("--follow is not supported when reading from a tar archive");
1347 goto bad_argument;
1348 }
1349
1350 /* done with argument parsing, do the actual work */
1351
1352 /* we have everything we need, start reading */
1353 if (private.archive_name)
1354 {
1355 /*
1356 * A NULL directory indicates that the archive file is located in the
1357 * current working directory.
1358 */
1359 if (private.archive_dir == NULL)
1360 private.archive_dir = pg_strdup(".");
1361
1362 /* Set up for reading tar file */
1363 init_archive_reader(&private, compression);
1364
1365 /* Routine to decode WAL files in tar archive */
1367 XLogReaderAllocate(private.segsize, private.archive_dir,
1368 XL_ROUTINE(.page_read = TarWALDumpReadPage,
1369 .segment_open = TarWALDumpOpenSegment,
1370 .segment_close = TarWALDumpCloseSegment),
1371 &private);
1372 }
1373 else
1374 {
1376 XLogReaderAllocate(private.segsize, waldir,
1377 XL_ROUTINE(.page_read = WALDumpReadPage,
1378 .segment_open = WALDumpOpenSegment,
1379 .segment_close = WALDumpCloseSegment),
1380 &private);
1381 }
1382
1383 if (!xlogreader_state)
1384 pg_fatal("out of memory while allocating a WAL reading processor");
1385
1386 /* first find a valid recptr to start from */
1388
1390 pg_fatal("could not find a valid record after %X/%08X",
1391 LSN_FORMAT_ARGS(private.startptr));
1392
1393 /*
1394 * Display a message that we're skipping data if `from` wasn't a pointer
1395 * to the start of a record and also wasn't a pointer to the beginning of
1396 * a segment (e.g. we were used in file mode).
1397 */
1398 if (first_record != private.startptr &&
1399 XLogSegmentOffset(private.startptr, private.segsize) != 0)
1400 pg_log_info(ngettext("first record is after %X/%08X, at %X/%08X, skipping over %u byte",
1401 "first record is after %X/%08X, at %X/%08X, skipping over %u bytes",
1402 (first_record - private.startptr)),
1403 LSN_FORMAT_ARGS(private.startptr),
1405 (uint32) (first_record - private.startptr));
1406
1407 if (config.stats == true && !config.quiet)
1408 stats.startptr = first_record;
1409
1410 /* Flag indicating that the decoding loop has been entered */
1411 private.decoding_started = true;
1412
1413 for (;;)
1414 {
1415 if (time_to_stop)
1416 {
1417 /* We've been Ctrl-C'ed, so leave */
1418 break;
1419 }
1420
1421 /* try to read the next record */
1422 record = XLogReadRecord(xlogreader_state, &errormsg);
1423 if (!record)
1424 {
1425 if (!config.follow || private.endptr_reached)
1426 break;
1427 else
1428 {
1429 pg_usleep(1000000L); /* 1 second */
1430 continue;
1431 }
1432 }
1433
1434 /* apply all specified filters */
1435 if (config.filter_by_rmgr_enabled &&
1436 !config.filter_by_rmgr[record->xl_rmid])
1437 continue;
1438
1439 if (config.filter_by_xid_enabled &&
1440 config.filter_by_xid != record->xl_xid)
1441 continue;
1442
1443 /* check for extended filtering */
1444 if (config.filter_by_extended &&
1447 config.filter_by_relation :
1453 continue;
1454
1456 continue;
1457
1458 /* perform any per-record work */
1459 if (!config.quiet)
1460 {
1461 if (config.stats == true)
1462 {
1464 stats.endptr = xlogreader_state->EndRecPtr;
1465 }
1466 else
1468 }
1469
1470 /* save full pages if requested */
1471 if (config.save_fullpage_path != NULL)
1473
1474 /* check whether we printed enough */
1476 if (config.stop_after_records > 0 &&
1478 break;
1479 }
1480
1481 if (config.stats == true && !config.quiet)
1482 XLogDumpDisplayStats(&config, &stats);
1483
1484 if (time_to_stop)
1485 exit(0);
1486
1487 if (errormsg)
1488 pg_fatal("error in WAL record at %X/%08X: %s",
1489 LSN_FORMAT_ARGS(xlogreader_state->ReadRecPtr),
1490 errormsg);
1491
1493
1494 if (private.archive_name)
1495 free_archive_reader(&private);
1496
1497 return EXIT_SUCCESS;
1498
1500 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
1501 return EXIT_FAILURE;
1502}
char * TmpWalSegDir
int read_archive_wal_page(XLogDumpPrivate *privateInfo, XLogRecPtr targetPagePtr, Size count, char *readBuff)
void free_archive_reader(XLogDumpPrivate *privateInfo)
void free_archive_wal_entry(const char *fname, XLogDumpPrivate *privateInfo)
void init_archive_reader(XLogDumpPrivate *privateInfo, pg_compress_algorithm compression)
uint32 BlockNumber
Definition block.h:31
#define InvalidBlockNumber
Definition block.h:33
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition block.h:71
PageData * Page
Definition bufpage.h:81
uint8_t uint8
Definition c.h:616
#define ngettext(s, p, n)
Definition c.h:1272
#define SIGNAL_ARGS
Definition c.h:1452
#define Assert(condition)
Definition c.h:945
#define PG_TEXTDOMAIN(domain)
Definition c.h:1305
#define PG_BINARY
Definition c.h:1376
uint64_t uint64
Definition c.h:619
uint32_t uint32
Definition c.h:618
#define PG_BINARY_W
Definition c.h:1379
uint32 TransactionId
Definition c.h:738
#define OidIsValid(objectId)
Definition c.h:860
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition exec.c:430
int main(void)
bool parse_tar_compress_algorithm(const char *fname, pg_compress_algorithm *algorithm)
Definition compression.c:49
pg_compress_algorithm
Definition compression.h:22
@ PG_COMPRESSION_NONE
Definition compression.h:23
int closedir(DIR *)
Definition dirent.c:127
struct dirent * readdir(DIR *)
Definition dirent.c:78
DIR * opendir(const char *)
Definition dirent.c:33
#define _(x)
Definition elog.c:95
char * pg_strdup(const char *in)
Definition fe_memutils.c:85
int pg_dir_create_mode
Definition file_perm.c:18
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition getopt_long.c:60
#define no_argument
Definition getopt_long.h:25
#define required_argument
Definition getopt_long.h:26
#define optional_argument
Definition getopt_long.h:27
long val
Definition informix.c:689
#define close(a)
Definition win32.h:12
#define read(a, b, c)
Definition win32.h:13
int i
Definition isn.c:77
void pg_logging_init(const char *argv0)
Definition logging.c:83
#define pg_log_error(...)
Definition logging.h:106
#define pg_log_error_hint(...)
Definition logging.h:112
#define pg_log_info(...)
Definition logging.h:124
#define pg_log_error_detail(...)
Definition logging.h:109
void pfree(void *pointer)
Definition mcxt.c:1616
char * pnstrdup(const char *in, Size len)
Definition mcxt.c:1792
#define pg_fatal(...)
#define MAXPGPATH
static char * filename
Definition pg_dumpall.c:133
PGDLLIMPORT int optind
Definition getopt.c:51
PGDLLIMPORT char * optarg
Definition getopt.c:53
char * datadir
static char buf[DEFAULT_XLOG_SEG_SIZE]
static void XLogDumpDisplayStats(XLogDumpConfig *config, XLogStats *stats)
Definition pg_waldump.c:739
static void TarWALDumpCloseSegment(XLogReaderState *state)
Definition pg_waldump.c:461
static void WALDumpCloseSegment(XLogReaderState *state)
Definition pg_waldump.c:399
static volatile sig_atomic_t time_to_stop
Definition pg_waldump.c:43
static void split_path(const char *path, char **dir, char **fname)
Definition pg_waldump.c:153
static int required_read_len(XLogDumpPrivate *private, XLogRecPtr targetPagePtr, int reqLen)
Definition pg_waldump.c:334
int open_file_in_directory(const char *directory, const char *fname)
Definition pg_waldump.c:180
static bool search_directory(const char *directory, const char *fname, int *WalSegSz)
Definition pg_waldump.c:202
static char * identify_target_directory(char *directory, char *fname, int *WalSegSz)
Definition pg_waldump.c:285
static void XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
Definition pg_waldump.c:659
static void XLogDumpStatsRow(const char *name, uint64 n, uint64 total_count, uint64 rec_len, uint64 total_rec_len, uint64 fpi_len, uint64 total_fpi_len, uint64 tot_len, uint64 total_len)
Definition pg_waldump.c:698
static void create_fullpage_directory(char *path)
Definition pg_waldump.c:120
static void TarWALDumpOpenSegment(XLogReaderState *state, XLogSegNo nextSegNo, TimeLineID *tli_p)
Definition pg_waldump.c:449
static bool verify_directory(const char *directory)
Definition pg_waldump.c:105
static const RelFileLocator emptyRelFileLocator
Definition pg_waldump.c:45
static void print_rmgr_list(void)
Definition pg_waldump.c:90
static void WALDumpOpenSegment(XLogReaderState *state, XLogSegNo nextSegNo, TimeLineID *tli_p)
Definition pg_waldump.c:357
static const char * progname
Definition pg_waldump.c:41
static void usage(void)
Definition pg_waldump.c:869
static int WALDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetPtr, char *readBuff)
Definition pg_waldump.c:408
static void XLogRecordSaveFPWs(XLogReaderState *record, const char *savepath)
Definition pg_waldump.c:603
static bool XLogRecordHasFPW(XLogReaderState *record)
Definition pg_waldump.c:582
static int TarWALDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetPtr, char *readBuff)
Definition pg_waldump.c:471
static void sigint_handler(SIGNAL_ARGS)
Definition pg_waldump.c:83
static bool XLogRecordMatchesRelationBlock(XLogReaderState *record, RelFileLocator matchRlocator, BlockNumber matchBlock, ForkNumber matchFork)
Definition pg_waldump.c:551
#define pqsignal
Definition port.h:547
int pg_mkdir_p(char *path, int omode)
Definition pgmkdirp.c:57
int pg_strcasecmp(const char *s1, const char *s2)
#define sprintf
Definition port.h:262
int pg_check_dir(const char *dir)
Definition pgcheckdir.c:33
#define snprintf
Definition port.h:260
const char * get_progname(const char *argv0)
Definition path.c:652
#define printf(...)
Definition port.h:266
static int fd(const char *x, int i)
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
#define RelFileLocatorEquals(locator1, locator2)
ForkNumber forkname_to_number(const char *forkName)
Definition relpath.c:50
const char *const forkNames[]
Definition relpath.c:33
ForkNumber
Definition relpath.h:56
@ InvalidForkNumber
Definition relpath.h:57
#define MAX_FORKNUM
Definition relpath.h:70
#define FORKNAMECHARS
Definition relpath.h:72
#define RelFileNumberIsValid(relnumber)
Definition relpath.h:27
#define RM_MAX_BUILTIN_ID
Definition rmgr.h:34
static bool RmgrIdIsCustom(int rmid)
Definition rmgr.h:48
#define RmgrIdIsValid(rmid)
Definition rmgr.h:53
#define RM_MAX_ID
Definition rmgr.h:33
const RmgrDescData * GetRmgrDesc(RmgrId rmid)
Definition rmgrdesc.c:87
#define EXIT_SUCCESS
Definition settings.h:193
#define EXIT_FAILURE
Definition settings.h:197
void pg_usleep(long microsec)
Definition signal.c:53
int WalSegSz
Definition streamutil.c:32
void resetStringInfo(StringInfo str)
Definition stringinfo.c:126
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
Definition dirent.c:26
RelFileNumber relNumber
const char * rm_name
Definition rmgrdesc.h:16
void(* rm_desc)(StringInfo buf, XLogReaderState *record)
Definition rmgrdesc.h:17
const char *(* rm_identify)(uint8 info)
Definition rmgrdesc.h:18
XLogSegNo ws_segno
Definition xlogreader.h:48
TimeLineID ws_tli
Definition xlogreader.h:49
bool filter_by_xid_enabled
Definition pg_waldump.c:62
RelFileLocator filter_by_relation
Definition pg_waldump.c:63
char * save_fullpage_path
Definition pg_waldump.c:72
bool filter_by_rmgr[RM_MAX_ID+1]
Definition pg_waldump.c:59
bool filter_by_extended
Definition pg_waldump.c:64
bool stats_per_record
Definition pg_waldump.c:56
int already_displayed_records
Definition pg_waldump.c:53
int stop_after_records
Definition pg_waldump.c:52
bool filter_by_relation_enabled
Definition pg_waldump.c:65
BlockNumber filter_by_relation_block
Definition pg_waldump.c:66
bool filter_by_rmgr_enabled
Definition pg_waldump.c:60
TransactionId filter_by_xid
Definition pg_waldump.c:61
ForkNumber filter_by_relation_forknum
Definition pg_waldump.c:68
bool filter_by_relation_block_enabled
Definition pg_waldump.c:67
char * errormsg_buf
Definition xlogreader.h:310
XLogRecPtr ReadRecPtr
Definition xlogreader.h:205
WALOpenSegment seg
Definition xlogreader.h:271
uint64 count
Definition xlogstats.h:23
uint64 fpi_len
Definition xlogstats.h:25
uint64 rec_len
Definition xlogstats.h:24
TransactionId xl_xid
Definition xlogrecord.h:44
RmgrId xl_rmid
Definition xlogrecord.h:47
XLogRecStats record_stats[RM_MAX_ID+1][MAX_XLINFO_TYPES]
Definition xlogstats.h:36
uint64 count
Definition xlogstats.h:30
XLogRecStats rmgr_stats[RM_MAX_ID+1]
Definition xlogstats.h:35
#define InvalidTransactionId
Definition transam.h:31
const char * name
#define IsValidWalSegSize(size)
XLogLongPageHeaderData * XLogLongPageHeader
#define XLogSegmentOffset(xlogptr, wal_segsz_bytes)
static bool IsXLogFileName(const char *fname)
static void XLogFromFileName(const char *fname, TimeLineID *tli, XLogSegNo *logSegNo, int wal_segsz_bytes)
#define XLogSegNoOffsetToRecPtr(segno, offset, wal_segsz_bytes, dest)
#define MAXFNAMELEN
#define XLOGDIR
#define XLByteToSeg(xlrp, logSegNo, wal_segsz_bytes)
static void XLogFileName(char *fname, TimeLineID tli, XLogSegNo logSegNo, int wal_segsz_bytes)
#define XLByteInSeg(xlrp, logSegNo, wal_segsz_bytes)
#define XLogRecPtrIsValid(r)
Definition xlogdefs.h:29
#define LSN_FORMAT_ARGS(lsn)
Definition xlogdefs.h:47
uint64 XLogRecPtr
Definition xlogdefs.h:21
#define InvalidXLogRecPtr
Definition xlogdefs.h:28
uint32 TimeLineID
Definition xlogdefs.h:63
uint64 XLogSegNo
Definition xlogdefs.h:52
void XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty, bool detailed_format, StringInfo buf, uint32 *fpi_len)
Definition xlogdesc.c:249
bool XLogRecGetBlockTagExtended(XLogReaderState *record, uint8 block_id, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum, Buffer *prefetch_buffer)
XLogReaderState * XLogReaderAllocate(int wal_segment_size, const char *waldir, XLogReaderRoutine *routine, void *private_data)
Definition xlogreader.c:108
bool WALRead(XLogReaderState *state, char *buf, XLogRecPtr startptr, Size count, TimeLineID tli, WALReadError *errinfo)
XLogRecord * XLogReadRecord(XLogReaderState *state, char **errormsg)
Definition xlogreader.c:391
void XLogReaderFree(XLogReaderState *state)
Definition xlogreader.c:163
XLogRecPtr XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr)
bool RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
#define XLogRecGetInfo(decoder)
Definition xlogreader.h:409
#define XLogRecGetRmid(decoder)
Definition xlogreader.h:410
#define XLogRecGetTotalLen(decoder)
Definition xlogreader.h:407
#define XLogRecGetXid(decoder)
Definition xlogreader.h:411
#define XL_ROUTINE(...)
Definition xlogreader.h:117
#define XLogRecMaxBlockId(decoder)
Definition xlogreader.h:417
#define XLogRecHasBlockImage(decoder, block_id)
Definition xlogreader.h:422
#define XLogRecHasBlockRef(decoder, block_id)
Definition xlogreader.h:419
#define XLogRecGetPrev(decoder)
Definition xlogreader.h:408
#define XLR_INFO_MASK
Definition xlogrecord.h:62
void XLogRecStoreStats(XLogStats *stats, XLogReaderState *record)
Definition xlogstats.c:54
void XLogRecGetLen(XLogReaderState *record, uint32 *rec_len, uint32 *fpi_len)
Definition xlogstats.c:22
#define MAX_XLINFO_TYPES
Definition xlogstats.h:19
static const char * directory
Definition zic.c:648