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