PostgreSQL Source Code  git master
pg_walinspect.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pg_walinspect.c
4  * Functions to inspect contents of PostgreSQL Write-Ahead Log
5  *
6  * Copyright (c) 2022-2023, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  * contrib/pg_walinspect/pg_walinspect.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14 
15 #include "access/xlog.h"
16 #include "access/xlog_internal.h"
17 #include "access/xlogreader.h"
18 #include "access/xlogrecovery.h"
19 #include "access/xlogstats.h"
20 #include "access/xlogutils.h"
21 #include "funcapi.h"
22 #include "miscadmin.h"
23 #include "utils/array.h"
24 #include "utils/builtins.h"
25 #include "utils/pg_lsn.h"
26 
27 /*
28  * NOTE: For any code change or issue fix here, it is highly recommended to
29  * give a thought about doing the same in pg_waldump tool as well.
30  */
31 
33 
40 
41 static void ValidateInputLSNs(XLogRecPtr start_lsn, XLogRecPtr *end_lsn);
42 static XLogRecPtr GetCurrentLSN(void);
45 static void GetWALRecordInfo(XLogReaderState *record, Datum *values,
46  bool *nulls, uint32 ncols);
47 static void GetWALRecordsInfo(FunctionCallInfo fcinfo,
48  XLogRecPtr start_lsn,
49  XLogRecPtr end_lsn);
50 static void GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
51  Datum *values, bool *nulls, uint32 ncols,
52  bool stats_per_record);
53 static void FillXLogStatsRow(const char *name, uint64 n, uint64 total_count,
54  uint64 rec_len, uint64 total_rec_len,
55  uint64 fpi_len, uint64 total_fpi_len,
56  uint64 tot_len, uint64 total_len,
57  Datum *values, bool *nulls, uint32 ncols);
58 static void GetWalStats(FunctionCallInfo fcinfo,
59  XLogRecPtr start_lsn,
60  XLogRecPtr end_lsn,
61  bool stats_per_record);
62 static void GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record);
63 
64 /*
65  * Return the LSN up to which the server has WAL.
66  */
67 static XLogRecPtr
69 {
70  XLogRecPtr curr_lsn;
71 
72  /*
73  * We determine the current LSN of the server similar to how page_read
74  * callback read_local_xlog_page_no_wait does.
75  */
76  if (!RecoveryInProgress())
77  curr_lsn = GetFlushRecPtr(NULL);
78  else
79  curr_lsn = GetXLogReplayRecPtr(NULL);
80 
81  Assert(!XLogRecPtrIsInvalid(curr_lsn));
82 
83  return curr_lsn;
84 }
85 
86 /*
87  * Intialize WAL reader and identify first valid LSN.
88  */
89 static XLogReaderState *
91 {
93  ReadLocalXLogPageNoWaitPrivate *private_data;
94  XLogRecPtr first_valid_record;
95 
96  /*
97  * Reading WAL below the first page of the first segments isn't allowed.
98  * This is a bootstrap WAL page and the page_read callback fails to read
99  * it.
100  */
101  if (lsn < XLOG_BLCKSZ)
102  ereport(ERROR,
103  (errmsg("could not read WAL at LSN %X/%X",
104  LSN_FORMAT_ARGS(lsn))));
105 
106  private_data = (ReadLocalXLogPageNoWaitPrivate *)
108 
111  .segment_open = &wal_segment_open,
112  .segment_close = &wal_segment_close),
113  private_data);
114 
115  if (xlogreader == NULL)
116  ereport(ERROR,
117  (errcode(ERRCODE_OUT_OF_MEMORY),
118  errmsg("out of memory"),
119  errdetail("Failed while allocating a WAL reading processor.")));
120 
121  /* first find a valid recptr to start from */
122  first_valid_record = XLogFindNextRecord(xlogreader, lsn);
123 
124  if (XLogRecPtrIsInvalid(first_valid_record))
125  ereport(ERROR,
126  (errmsg("could not find a valid record after %X/%X",
127  LSN_FORMAT_ARGS(lsn))));
128 
129  return xlogreader;
130 }
131 
132 /*
133  * Read next WAL record.
134  *
135  * By design, to be less intrusive in a running system, no slot is allocated
136  * to reserve the WAL we're about to read. Therefore this function can
137  * encounter read errors for historical WAL.
138  *
139  * We guard against ordinary errors trying to read WAL that hasn't been
140  * written yet by limiting end_lsn to the flushed WAL, but that can also
141  * encounter errors if the flush pointer falls in the middle of a record. In
142  * that case we'll return NULL.
143  */
144 static XLogRecord *
146 {
147  XLogRecord *record;
148  char *errormsg;
149 
150  record = XLogReadRecord(xlogreader, &errormsg);
151 
152  if (record == NULL)
153  {
154  ReadLocalXLogPageNoWaitPrivate *private_data;
155 
156  /* return NULL, if end of WAL is reached */
157  private_data = (ReadLocalXLogPageNoWaitPrivate *)
159 
160  if (private_data->end_of_wal)
161  return NULL;
162 
163  if (errormsg)
164  ereport(ERROR,
166  errmsg("could not read WAL at %X/%X: %s",
167  LSN_FORMAT_ARGS(xlogreader->EndRecPtr), errormsg)));
168  else
169  ereport(ERROR,
171  errmsg("could not read WAL at %X/%X",
173  }
174 
175  return record;
176 }
177 
178 /*
179  * Get a single WAL record info.
180  */
181 static void
183  bool *nulls, uint32 ncols)
184 {
185  const char *id;
186  RmgrData desc;
187  uint32 fpi_len = 0;
188  StringInfoData rec_desc;
189  StringInfoData rec_blk_ref;
190  uint32 main_data_len;
191  int i = 0;
192 
193  desc = GetRmgr(XLogRecGetRmid(record));
194  id = desc.rm_identify(XLogRecGetInfo(record));
195 
196  if (id == NULL)
197  id = psprintf("UNKNOWN (%x)", XLogRecGetInfo(record) & ~XLR_INFO_MASK);
198 
199  initStringInfo(&rec_desc);
200  desc.rm_desc(&rec_desc, record);
201 
202  /* Block references. */
203  initStringInfo(&rec_blk_ref);
204  XLogRecGetBlockRefInfo(record, false, true, &rec_blk_ref, &fpi_len);
205 
206  main_data_len = XLogRecGetDataLen(record);
207 
208  values[i++] = LSNGetDatum(record->ReadRecPtr);
209  values[i++] = LSNGetDatum(record->EndRecPtr);
210  values[i++] = LSNGetDatum(XLogRecGetPrev(record));
212  values[i++] = CStringGetTextDatum(desc.rm_name);
213  values[i++] = CStringGetTextDatum(id);
215  values[i++] = UInt32GetDatum(main_data_len);
216  values[i++] = UInt32GetDatum(fpi_len);
217  values[i++] = CStringGetTextDatum(rec_desc.data);
218  values[i++] = CStringGetTextDatum(rec_blk_ref.data);
219 
220  Assert(i == ncols);
221 }
222 
223 
224 /*
225  * Store a set of block information from a single record (FPI and block
226  * information).
227  */
228 static void
230 {
231 #define PG_GET_WAL_BLOCK_INFO_COLS 11
232  int block_id;
233  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
234 
235  for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
236  {
237  DecodedBkpBlock *blk;
238  BlockNumber blkno;
239  RelFileLocator rnode;
240  ForkNumber fork;
242  bool nulls[PG_GET_WAL_BLOCK_INFO_COLS] = {0};
243  int i = 0;
244 
245  if (!XLogRecHasBlockRef(record, block_id))
246  continue;
247 
248  blk = XLogRecGetBlock(record, block_id);
249 
250  (void) XLogRecGetBlockTagExtended(record, block_id,
251  &rnode, &fork, &blkno, NULL);
252 
253  values[i++] = LSNGetDatum(record->ReadRecPtr);
254  values[i++] = Int16GetDatum(block_id);
258  values[i++] = Int64GetDatum((int64) blkno);
259 
260  if (fork >= 0 && fork <= MAX_FORKNUM)
262  else
263  ereport(ERROR,
264  (errcode(ERRCODE_INTERNAL_ERROR),
265  errmsg_internal("invalid fork number: %u", fork)));
266 
267  /* Block data */
268  if (blk->has_data)
269  {
270  bytea *raw_data;
271 
272  /* Initialize bytea buffer to copy the data to */
273  raw_data = (bytea *) palloc(blk->data_len + VARHDRSZ);
274  SET_VARSIZE(raw_data, blk->data_len + VARHDRSZ);
275 
276  /* Copy the data */
277  memcpy(VARDATA(raw_data), blk->data, blk->data_len);
278  values[i++] = PointerGetDatum(raw_data);
279  }
280  else
281  {
282  /* No data, so set this field to NULL */
283  nulls[i++] = true;
284  }
285 
286  if (blk->has_image)
287  {
289  Page page;
290  bytea *raw_page;
291  int bitcnt;
292  int cnt = 0;
293  Datum *flags;
294  ArrayType *a;
295 
296  page = (Page) buf.data;
297 
298  /* Full page image exists, so let's save it */
299  if (!RestoreBlockImage(record, block_id, page))
300  ereport(ERROR,
301  (errcode(ERRCODE_INTERNAL_ERROR),
302  errmsg_internal("%s", record->errormsg_buf)));
303 
304  /* Initialize bytea buffer to copy the FPI to */
305  raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
306  SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
307 
308  /* Take a verbatim copy of the FPI */
309  memcpy(VARDATA(raw_page), page, BLCKSZ);
310 
311  values[i++] = PointerGetDatum(raw_page);
312  values[i++] = UInt32GetDatum(blk->bimg_len);
313 
314  /* FPI flags */
315  bitcnt = pg_popcount((const char *) &blk->bimg_info,
316  sizeof(uint8));
317  /* Build set of raw flags */
318  flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
319 
320  if ((blk->bimg_info & BKPIMAGE_HAS_HOLE) != 0)
321  flags[cnt++] = CStringGetTextDatum("HAS_HOLE");
322  if (blk->apply_image)
323  flags[cnt++] = CStringGetTextDatum("APPLY");
324  if ((blk->bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
325  flags[cnt++] = CStringGetTextDatum("COMPRESS_PGLZ");
326  if ((blk->bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
327  flags[cnt++] = CStringGetTextDatum("COMPRESS_LZ4");
328  if ((blk->bimg_info & BKPIMAGE_COMPRESS_ZSTD) != 0)
329  flags[cnt++] = CStringGetTextDatum("COMPRESS_ZSTD");
330 
331  Assert(cnt <= bitcnt);
332  a = construct_array_builtin(flags, cnt, TEXTOID);
333  values[i++] = PointerGetDatum(a);
334  }
335  else
336  {
337  /* No full page image, so store NULLs for all its fields */
338  memset(&nulls[i], true, 3 * sizeof(bool));
339  i += 3;
340  }
341 
343 
344  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
345  values, nulls);
346  }
347 
348 #undef PG_GET_WAL_FPI_BLOCK_COLS
349 }
350 
351 /*
352  * Get information about all the blocks saved in WAL records between start
353  * and end LSNs. This produces information about the full page images with
354  * their relation information, and the data saved in each block associated
355  * to a record. Decompression is applied to the full page images, if
356  * necessary.
357  */
358 Datum
360 {
361  XLogRecPtr start_lsn = PG_GETARG_LSN(0);
362  XLogRecPtr end_lsn = PG_GETARG_LSN(1);
364  MemoryContext old_cxt;
365  MemoryContext tmp_cxt;
366 
367  ValidateInputLSNs(start_lsn, &end_lsn);
368 
369  InitMaterializedSRF(fcinfo, 0);
370 
371  xlogreader = InitXLogReaderState(start_lsn);
372 
374  "pg_get_block_fpi_info temporary cxt",
376 
377  while (ReadNextXLogRecord(xlogreader) &&
378  xlogreader->EndRecPtr <= end_lsn)
379  {
380  /* Use the tmp context so we can clean up after each tuple is done */
381  old_cxt = MemoryContextSwitchTo(tmp_cxt);
382 
383  GetWALBlockInfo(fcinfo, xlogreader);
384 
385  /* clean up and switch back */
386  MemoryContextSwitchTo(old_cxt);
387  MemoryContextReset(tmp_cxt);
388 
390  }
391 
392  MemoryContextDelete(tmp_cxt);
395 
396  PG_RETURN_VOID();
397 }
398 
399 /*
400  * Get WAL record info.
401  */
402 Datum
404 {
405 #define PG_GET_WAL_RECORD_INFO_COLS 11
406  Datum result;
408  bool nulls[PG_GET_WAL_RECORD_INFO_COLS] = {0};
409  XLogRecPtr lsn;
410  XLogRecPtr curr_lsn;
412  TupleDesc tupdesc;
413  HeapTuple tuple;
414 
415  lsn = PG_GETARG_LSN(0);
416  curr_lsn = GetCurrentLSN();
417 
418  if (lsn > curr_lsn)
419  ereport(ERROR,
420  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
421  errmsg("WAL input LSN must be less than current LSN"),
422  errdetail("Current WAL LSN on the database system is at %X/%X.",
423  LSN_FORMAT_ARGS(curr_lsn))));
424 
425  /* Build a tuple descriptor for our result type. */
426  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
427  elog(ERROR, "return type must be a row type");
428 
430 
432  ereport(ERROR,
433  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
434  errmsg("could not read WAL at %X/%X",
436 
438 
441 
442  tuple = heap_form_tuple(tupdesc, values, nulls);
443  result = HeapTupleGetDatum(tuple);
444 
445  PG_RETURN_DATUM(result);
446 #undef PG_GET_WAL_RECORD_INFO_COLS
447 }
448 
449 /*
450  * Validate start and end LSNs coming from the function inputs.
451  *
452  * If end_lsn is found to be higher than the current LSN reported by the
453  * cluster, use the current LSN as the upper bound.
454  */
455 static void
457 {
458  XLogRecPtr curr_lsn = GetCurrentLSN();
459 
460  if (start_lsn > curr_lsn)
461  ereport(ERROR,
462  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
463  errmsg("WAL start LSN must be less than current LSN"),
464  errdetail("Current WAL LSN on the database system is at %X/%X.",
465  LSN_FORMAT_ARGS(curr_lsn))));
466 
467  if (start_lsn > *end_lsn)
468  ereport(ERROR,
469  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
470  errmsg("WAL start LSN must be less than end LSN")));
471 
472  if (*end_lsn > curr_lsn)
473  *end_lsn = curr_lsn;
474 }
475 
476 /*
477  * Get info and data of all WAL records between start LSN and end LSN.
478  */
479 static void
481  XLogRecPtr end_lsn)
482 {
483 #define PG_GET_WAL_RECORDS_INFO_COLS 11
485  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
487  bool nulls[PG_GET_WAL_RECORDS_INFO_COLS] = {0};
488  MemoryContext old_cxt;
489  MemoryContext tmp_cxt;
490 
491  Assert(start_lsn <= end_lsn);
492 
493  InitMaterializedSRF(fcinfo, 0);
494 
495  xlogreader = InitXLogReaderState(start_lsn);
496 
498  "GetWALRecordsInfo temporary cxt",
500 
501  while (ReadNextXLogRecord(xlogreader) &&
502  xlogreader->EndRecPtr <= end_lsn)
503  {
504  /* Use the tmp context so we can clean up after each tuple is done */
505  old_cxt = MemoryContextSwitchTo(tmp_cxt);
506 
509 
510  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
511  values, nulls);
512 
513  /* clean up and switch back */
514  MemoryContextSwitchTo(old_cxt);
515  MemoryContextReset(tmp_cxt);
516 
518  }
519 
520  MemoryContextDelete(tmp_cxt);
523 
524 #undef PG_GET_WAL_RECORDS_INFO_COLS
525 }
526 
527 /*
528  * Get info and data of all WAL records between start LSN and end LSN.
529  */
530 Datum
532 {
533  XLogRecPtr start_lsn = PG_GETARG_LSN(0);
534  XLogRecPtr end_lsn = PG_GETARG_LSN(1);
535 
536  ValidateInputLSNs(start_lsn, &end_lsn);
537  GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
538 
539  PG_RETURN_VOID();
540 }
541 
542 /*
543  * Fill single row of record counts and sizes for an rmgr or record.
544  */
545 static void
546 FillXLogStatsRow(const char *name,
547  uint64 n, uint64 total_count,
548  uint64 rec_len, uint64 total_rec_len,
549  uint64 fpi_len, uint64 total_fpi_len,
550  uint64 tot_len, uint64 total_len,
551  Datum *values, bool *nulls, uint32 ncols)
552 {
553  double n_pct,
554  rec_len_pct,
555  fpi_len_pct,
556  tot_len_pct;
557  int i = 0;
558 
559  n_pct = 0;
560  if (total_count != 0)
561  n_pct = 100 * (double) n / total_count;
562 
563  rec_len_pct = 0;
564  if (total_rec_len != 0)
565  rec_len_pct = 100 * (double) rec_len / total_rec_len;
566 
567  fpi_len_pct = 0;
568  if (total_fpi_len != 0)
569  fpi_len_pct = 100 * (double) fpi_len / total_fpi_len;
570 
571  tot_len_pct = 0;
572  if (total_len != 0)
573  tot_len_pct = 100 * (double) tot_len / total_len;
574 
576  values[i++] = Int64GetDatum(n);
577  values[i++] = Float8GetDatum(n_pct);
578  values[i++] = Int64GetDatum(rec_len);
579  values[i++] = Float8GetDatum(rec_len_pct);
580  values[i++] = Int64GetDatum(fpi_len);
581  values[i++] = Float8GetDatum(fpi_len_pct);
582  values[i++] = Int64GetDatum(tot_len);
583  values[i++] = Float8GetDatum(tot_len_pct);
584 
585  Assert(i == ncols);
586 }
587 
588 /*
589  * Get summary statistics about the records seen so far.
590  */
591 static void
593  Datum *values, bool *nulls, uint32 ncols,
594  bool stats_per_record)
595 {
596  MemoryContext old_cxt;
597  MemoryContext tmp_cxt;
598  uint64 total_count = 0;
599  uint64 total_rec_len = 0;
600  uint64 total_fpi_len = 0;
601  uint64 total_len = 0;
602  int ri;
603 
604  /*
605  * Each row shows its percentages of the total, so make a first pass to
606  * calculate column totals.
607  */
608  for (ri = 0; ri <= RM_MAX_ID; ri++)
609  {
610  if (!RmgrIdIsValid(ri))
611  continue;
612 
613  total_count += stats->rmgr_stats[ri].count;
614  total_rec_len += stats->rmgr_stats[ri].rec_len;
615  total_fpi_len += stats->rmgr_stats[ri].fpi_len;
616  }
617  total_len = total_rec_len + total_fpi_len;
618 
620  "GetXLogSummaryStats temporary cxt",
622 
623  for (ri = 0; ri <= RM_MAX_ID; ri++)
624  {
625  uint64 count;
626  uint64 rec_len;
627  uint64 fpi_len;
628  uint64 tot_len;
629  RmgrData desc;
630 
631  if (!RmgrIdIsValid(ri))
632  continue;
633 
634  if (!RmgrIdExists(ri))
635  continue;
636 
637  desc = GetRmgr(ri);
638 
639  if (stats_per_record)
640  {
641  int rj;
642 
643  for (rj = 0; rj < MAX_XLINFO_TYPES; rj++)
644  {
645  const char *id;
646 
647  count = stats->record_stats[ri][rj].count;
648  rec_len = stats->record_stats[ri][rj].rec_len;
649  fpi_len = stats->record_stats[ri][rj].fpi_len;
650  tot_len = rec_len + fpi_len;
651 
652  /* Skip undefined combinations and ones that didn't occur */
653  if (count == 0)
654  continue;
655 
656  old_cxt = MemoryContextSwitchTo(tmp_cxt);
657 
658  /* the upper four bits in xl_info are the rmgr's */
659  id = desc.rm_identify(rj << 4);
660  if (id == NULL)
661  id = psprintf("UNKNOWN (%x)", rj << 4);
662 
663  FillXLogStatsRow(psprintf("%s/%s", desc.rm_name, id), count,
664  total_count, rec_len, total_rec_len, fpi_len,
665  total_fpi_len, tot_len, total_len,
666  values, nulls, ncols);
667 
668  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
669  values, nulls);
670 
671  /* clean up and switch back */
672  MemoryContextSwitchTo(old_cxt);
673  MemoryContextReset(tmp_cxt);
674  }
675  }
676  else
677  {
678  count = stats->rmgr_stats[ri].count;
679  rec_len = stats->rmgr_stats[ri].rec_len;
680  fpi_len = stats->rmgr_stats[ri].fpi_len;
681  tot_len = rec_len + fpi_len;
682 
683  old_cxt = MemoryContextSwitchTo(tmp_cxt);
684 
685  FillXLogStatsRow(desc.rm_name, count, total_count, rec_len,
686  total_rec_len, fpi_len, total_fpi_len, tot_len,
687  total_len, values, nulls, ncols);
688 
689  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
690  values, nulls);
691 
692  /* clean up and switch back */
693  MemoryContextSwitchTo(old_cxt);
694  MemoryContextReset(tmp_cxt);
695  }
696  }
697 
698  MemoryContextDelete(tmp_cxt);
699 }
700 
701 /*
702  * Get WAL stats between start LSN and end LSN.
703  */
704 static void
706  bool stats_per_record)
707 {
708 #define PG_GET_WAL_STATS_COLS 9
710  XLogStats stats = {0};
711  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
713  bool nulls[PG_GET_WAL_STATS_COLS] = {0};
714 
715  Assert(start_lsn <= end_lsn);
716 
717  InitMaterializedSRF(fcinfo, 0);
718 
719  xlogreader = InitXLogReaderState(start_lsn);
720 
721  while (ReadNextXLogRecord(xlogreader) &&
722  xlogreader->EndRecPtr <= end_lsn)
723  {
724  XLogRecStoreStats(&stats, xlogreader);
725 
727  }
728 
731 
732  GetXLogSummaryStats(&stats, rsinfo, values, nulls,
734  stats_per_record);
735 
736 #undef PG_GET_WAL_STATS_COLS
737 }
738 
739 /*
740  * Get stats of all WAL records between start LSN and end LSN.
741  */
742 Datum
744 {
745  XLogRecPtr start_lsn = PG_GETARG_LSN(0);
746  XLogRecPtr end_lsn = PG_GETARG_LSN(1);
747  bool stats_per_record = PG_GETARG_BOOL(2);
748 
749  ValidateInputLSNs(start_lsn, &end_lsn);
750  GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
751 
752  PG_RETURN_VOID();
753 }
754 
755 /*
756  * The following functions have been removed in newer versions in 1.1, but
757  * they are kept around for compatibility.
758  */
759 Datum
761 {
762  XLogRecPtr start_lsn = PG_GETARG_LSN(0);
763  XLogRecPtr end_lsn = GetCurrentLSN();
764 
765  if (start_lsn > end_lsn)
766  ereport(ERROR,
767  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
768  errmsg("WAL start LSN must be less than current LSN"),
769  errdetail("Current WAL LSN on the database system is at %X/%X.",
770  LSN_FORMAT_ARGS(end_lsn))));
771 
772  GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
773 
774  PG_RETURN_VOID();
775 }
776 
777 Datum
779 {
780  XLogRecPtr start_lsn = PG_GETARG_LSN(0);
781  XLogRecPtr end_lsn = GetCurrentLSN();
782  bool stats_per_record = PG_GETARG_BOOL(1);
783 
784  if (start_lsn > end_lsn)
785  ereport(ERROR,
786  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
787  errmsg("WAL start LSN must be less than current LSN"),
788  errdetail("Current WAL LSN on the database system is at %X/%X.",
789  LSN_FORMAT_ARGS(end_lsn))));
790 
791  GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
792 
793  PG_RETURN_VOID();
794 }
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3363
uint32 BlockNumber
Definition: block.h:31
static Datum values[MAXATTR]
Definition: bootstrap.c:156
Pointer Page
Definition: bufpage.h:78
#define CStringGetTextDatum(s)
Definition: builtins.h:94
unsigned int uint32
Definition: c.h:490
#define VARHDRSZ
Definition: c.h:676
unsigned char uint8
Definition: c.h:488
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1156
int errcode_for_file_access(void)
Definition: elog.c:881
int errdetail(const char *fmt,...)
Definition: elog.c:1202
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
const char * name
Definition: encode.c:571
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1794
Datum Float8GetDatum(float8 X)
Definition: fmgr.c:1803
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition: funcapi.c:76
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:276
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition: funcapi.h:230
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
int a
Definition: isn.c:69
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
Assert(fmt[strlen(fmt) - 1] !='\n')
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:314
void pfree(void *pointer)
Definition: mcxt.c:1436
void * palloc0(Size size)
Definition: mcxt.c:1241
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:387
void * palloc(Size size)
Definition: mcxt.c:1210
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
uint64 pg_popcount(const char *buf, int bytes)
Definition: pg_bitutils.c:296
#define PG_GETARG_LSN(n)
Definition: pg_lsn.h:33
static Datum LSNGetDatum(XLogRecPtr X)
Definition: pg_lsn.h:28
static char * buf
Definition: pg_test_fsync.c:67
static void GetWALRecordInfo(XLogReaderState *record, Datum *values, bool *nulls, uint32 ncols)
#define PG_GET_WAL_STATS_COLS
Datum pg_get_wal_records_info(PG_FUNCTION_ARGS)
Datum pg_get_wal_stats(PG_FUNCTION_ARGS)
static void GetWalStats(FunctionCallInfo fcinfo, XLogRecPtr start_lsn, XLogRecPtr end_lsn, bool stats_per_record)
#define PG_GET_WAL_RECORDS_INFO_COLS
static void GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record)
#define PG_GET_WAL_RECORD_INFO_COLS
Datum pg_get_wal_block_info(PG_FUNCTION_ARGS)
static void GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn, XLogRecPtr end_lsn)
Datum pg_get_wal_records_info_till_end_of_wal(PG_FUNCTION_ARGS)
PG_MODULE_MAGIC
Definition: pg_walinspect.c:32
#define PG_GET_WAL_BLOCK_INFO_COLS
static XLogRecord * ReadNextXLogRecord(XLogReaderState *xlogreader)
static XLogReaderState * InitXLogReaderState(XLogRecPtr lsn)
Definition: pg_walinspect.c:90
Datum pg_get_wal_stats_till_end_of_wal(PG_FUNCTION_ARGS)
static void GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo, Datum *values, bool *nulls, uint32 ncols, bool stats_per_record)
PG_FUNCTION_INFO_V1(pg_get_wal_block_info)
static void FillXLogStatsRow(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, Datum *values, bool *nulls, uint32 ncols)
static void ValidateInputLSNs(XLogRecPtr start_lsn, XLogRecPtr *end_lsn)
Datum pg_get_wal_record_info(PG_FUNCTION_ARGS)
static XLogRecPtr GetCurrentLSN(void)
Definition: pg_walinspect.c:68
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
static Datum TransactionIdGetDatum(TransactionId X)
Definition: postgres.h:272
uintptr_t Datum
Definition: postgres.h:64
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum UInt32GetDatum(uint32 X)
Definition: postgres.h:232
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
const char *const forkNames[]
Definition: relpath.c:33
ForkNumber
Definition: relpath.h:48
#define MAX_FORKNUM
Definition: relpath.h:62
#define RmgrIdIsValid(rmid)
Definition: rmgr.h:53
#define RM_MAX_ID
Definition: rmgr.h:33
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
RelFileLocator rlocator
Definition: xlogreader.h:125
fmNodePtr resultinfo
Definition: fmgr.h:89
RelFileNumber relNumber
TupleDesc setDesc
Definition: execnodes.h:334
Tuplestorestate * setResult
Definition: execnodes.h:333
const char *(* rm_identify)(uint8 info)
const char * rm_name
void(* rm_desc)(StringInfo buf, XLogReaderState *record)
char * errormsg_buf
Definition: xlogreader.h:311
XLogRecPtr EndRecPtr
Definition: xlogreader.h:207
XLogRecPtr ReadRecPtr
Definition: xlogreader.h:206
void * private_data
Definition: xlogreader.h:196
uint64 count
Definition: xlogstats.h:23
uint64 fpi_len
Definition: xlogstats.h:25
uint64 rec_len
Definition: xlogstats.h:24
XLogRecStats record_stats[RM_MAX_ID+1][MAX_XLINFO_TYPES]
Definition: xlogstats.h:36
XLogRecStats rmgr_stats[RM_MAX_ID+1]
Definition: xlogstats.h:35
Definition: c.h:671
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull)
Definition: tuplestore.c:750
#define VARDATA(PTR)
Definition: varatt.h:278
#define SET_VARSIZE(PTR, len)
Definition: varatt.h:305
bool RecoveryInProgress(void)
Definition: xlog.c:5908
int wal_segment_size
Definition: xlog.c:146
XLogRecPtr GetFlushRecPtr(TimeLineID *insertTLI)
Definition: xlog.c:6073
static RmgrData GetRmgr(RmgrId rmid)
static bool RmgrIdExists(RmgrId rmid)
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:43
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29
uint64 XLogRecPtr
Definition: xlogdefs.h:21
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
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 XLogRecGetDataLen(decoder)
Definition: xlogreader.h:415
#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 XLogRecGetBlock(decoder, i)
Definition: xlogreader.h:418
#define XL_ROUTINE(...)
Definition: xlogreader.h:117
#define XLogRecMaxBlockId(decoder)
Definition: xlogreader.h:417
#define XLogRecHasBlockRef(decoder, block_id)
Definition: xlogreader.h:419
#define XLogRecGetPrev(decoder)
Definition: xlogreader.h:408
#define BKPIMAGE_COMPRESS_ZSTD
Definition: xlogrecord.h:151
#define BKPIMAGE_HAS_HOLE
Definition: xlogrecord.h:146
#define BKPIMAGE_COMPRESS_LZ4
Definition: xlogrecord.h:150
#define XLR_INFO_MASK
Definition: xlogrecord.h:62
#define BKPIMAGE_COMPRESS_PGLZ
Definition: xlogrecord.h:149
XLogRecPtr GetXLogReplayRecPtr(TimeLineID *replayTLI)
static XLogReaderState * xlogreader
Definition: xlogrecovery.c:188
void XLogRecStoreStats(XLogStats *stats, XLogReaderState *record)
Definition: xlogstats.c:54
#define MAX_XLINFO_TYPES
Definition: xlogstats.h:19
void wal_segment_close(XLogReaderState *state)
Definition: xlogutils.c:859
void wal_segment_open(XLogReaderState *state, XLogSegNo nextSegNo, TimeLineID *tli_p)
Definition: xlogutils.c:834
int read_local_xlog_page_no_wait(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
Definition: xlogutils.c:890