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-2024, 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  bool show_data);
64 
65 /*
66  * Return the LSN up to which the server has WAL.
67  */
68 static XLogRecPtr
70 {
71  XLogRecPtr curr_lsn;
72 
73  /*
74  * We determine the current LSN of the server similar to how page_read
75  * callback read_local_xlog_page_no_wait does.
76  */
77  if (!RecoveryInProgress())
78  curr_lsn = GetFlushRecPtr(NULL);
79  else
80  curr_lsn = GetXLogReplayRecPtr(NULL);
81 
82  Assert(!XLogRecPtrIsInvalid(curr_lsn));
83 
84  return curr_lsn;
85 }
86 
87 /*
88  * Initialize WAL reader and identify first valid LSN.
89  */
90 static XLogReaderState *
92 {
94  ReadLocalXLogPageNoWaitPrivate *private_data;
95  XLogRecPtr first_valid_record;
96 
97  /*
98  * Reading WAL below the first page of the first segments isn't allowed.
99  * This is a bootstrap WAL page and the page_read callback fails to read
100  * it.
101  */
102  if (lsn < XLOG_BLCKSZ)
103  ereport(ERROR,
104  (errmsg("could not read WAL at LSN %X/%X",
105  LSN_FORMAT_ARGS(lsn))));
106 
107  private_data = (ReadLocalXLogPageNoWaitPrivate *)
109 
112  .segment_open = &wal_segment_open,
113  .segment_close = &wal_segment_close),
114  private_data);
115 
116  if (xlogreader == NULL)
117  ereport(ERROR,
118  (errcode(ERRCODE_OUT_OF_MEMORY),
119  errmsg("out of memory"),
120  errdetail("Failed while allocating a WAL reading processor.")));
121 
122  /* first find a valid recptr to start from */
123  first_valid_record = XLogFindNextRecord(xlogreader, lsn);
124 
125  if (XLogRecPtrIsInvalid(first_valid_record))
126  ereport(ERROR,
127  (errmsg("could not find a valid record after %X/%X",
128  LSN_FORMAT_ARGS(lsn))));
129 
130  return xlogreader;
131 }
132 
133 /*
134  * Read next WAL record.
135  *
136  * By design, to be less intrusive in a running system, no slot is allocated
137  * to reserve the WAL we're about to read. Therefore this function can
138  * encounter read errors for historical WAL.
139  *
140  * We guard against ordinary errors trying to read WAL that hasn't been
141  * written yet by limiting end_lsn to the flushed WAL, but that can also
142  * encounter errors if the flush pointer falls in the middle of a record. In
143  * that case we'll return NULL.
144  */
145 static XLogRecord *
147 {
148  XLogRecord *record;
149  char *errormsg;
150 
151  record = XLogReadRecord(xlogreader, &errormsg);
152 
153  if (record == NULL)
154  {
155  ReadLocalXLogPageNoWaitPrivate *private_data;
156 
157  /* return NULL, if end of WAL is reached */
158  private_data = (ReadLocalXLogPageNoWaitPrivate *)
160 
161  if (private_data->end_of_wal)
162  return NULL;
163 
164  if (errormsg)
165  ereport(ERROR,
167  errmsg("could not read WAL at %X/%X: %s",
168  LSN_FORMAT_ARGS(xlogreader->EndRecPtr), errormsg)));
169  else
170  ereport(ERROR,
172  errmsg("could not read WAL at %X/%X",
174  }
175 
176  return record;
177 }
178 
179 /*
180  * Output values that make up a row describing caller's WAL record.
181  *
182  * This function leaks memory. Caller may need to use its own custom memory
183  * context.
184  *
185  * Keep this in sync with GetWALBlockInfo.
186  */
187 static void
189  bool *nulls, uint32 ncols)
190 {
191  const char *record_type;
192  RmgrData desc;
193  uint32 fpi_len = 0;
194  StringInfoData rec_desc;
195  StringInfoData rec_blk_ref;
196  int i = 0;
197 
198  desc = GetRmgr(XLogRecGetRmid(record));
199  record_type = desc.rm_identify(XLogRecGetInfo(record));
200 
201  if (record_type == NULL)
202  record_type = psprintf("UNKNOWN (%x)", XLogRecGetInfo(record) & ~XLR_INFO_MASK);
203 
204  initStringInfo(&rec_desc);
205  desc.rm_desc(&rec_desc, record);
206 
207  if (XLogRecHasAnyBlockRefs(record))
208  {
209  initStringInfo(&rec_blk_ref);
210  XLogRecGetBlockRefInfo(record, false, true, &rec_blk_ref, &fpi_len);
211  }
212 
213  values[i++] = LSNGetDatum(record->ReadRecPtr);
214  values[i++] = LSNGetDatum(record->EndRecPtr);
215  values[i++] = LSNGetDatum(XLogRecGetPrev(record));
217  values[i++] = CStringGetTextDatum(desc.rm_name);
218  values[i++] = CStringGetTextDatum(record_type);
221  values[i++] = UInt32GetDatum(fpi_len);
222 
223  if (rec_desc.len > 0)
224  values[i++] = CStringGetTextDatum(rec_desc.data);
225  else
226  nulls[i++] = true;
227 
228  if (XLogRecHasAnyBlockRefs(record))
229  values[i++] = CStringGetTextDatum(rec_blk_ref.data);
230  else
231  nulls[i++] = true;
232 
233  Assert(i == ncols);
234 }
235 
236 
237 /*
238  * Output one or more rows in rsinfo tuple store, each describing a single
239  * block reference from caller's WAL record. (Should only be called with
240  * records that have block references.)
241  *
242  * This function leaks memory. Caller may need to use its own custom memory
243  * context.
244  *
245  * Keep this in sync with GetWALRecordInfo.
246  */
247 static void
249  bool show_data)
250 {
251 #define PG_GET_WAL_BLOCK_INFO_COLS 20
252  int block_id;
253  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
254  RmgrData desc;
255  const char *record_type;
256  StringInfoData rec_desc;
257 
259 
260  desc = GetRmgr(XLogRecGetRmid(record));
261  record_type = desc.rm_identify(XLogRecGetInfo(record));
262 
263  if (record_type == NULL)
264  record_type = psprintf("UNKNOWN (%x)",
265  XLogRecGetInfo(record) & ~XLR_INFO_MASK);
266 
267  initStringInfo(&rec_desc);
268  desc.rm_desc(&rec_desc, record);
269 
270  for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
271  {
272  DecodedBkpBlock *blk;
273  BlockNumber blkno;
274  RelFileLocator rnode;
275  ForkNumber forknum;
277  bool nulls[PG_GET_WAL_BLOCK_INFO_COLS] = {0};
278  uint32 block_data_len = 0,
279  block_fpi_len = 0;
280  ArrayType *block_fpi_info = NULL;
281  int i = 0;
282 
283  if (!XLogRecHasBlockRef(record, block_id))
284  continue;
285 
286  blk = XLogRecGetBlock(record, block_id);
287 
288  (void) XLogRecGetBlockTagExtended(record, block_id,
289  &rnode, &forknum, &blkno, NULL);
290 
291  /* Save block_data_len */
292  if (blk->has_data)
293  block_data_len = blk->data_len;
294 
295  if (blk->has_image)
296  {
297  /* Block reference has an FPI, so prepare relevant output */
298  int bitcnt;
299  int cnt = 0;
300  Datum *flags;
301 
302  /* Save block_fpi_len */
303  block_fpi_len = blk->bimg_len;
304 
305  /* Construct and save block_fpi_info */
306  bitcnt = pg_popcount((const char *) &blk->bimg_info,
307  sizeof(uint8));
308  flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
309  if ((blk->bimg_info & BKPIMAGE_HAS_HOLE) != 0)
310  flags[cnt++] = CStringGetTextDatum("HAS_HOLE");
311  if (blk->apply_image)
312  flags[cnt++] = CStringGetTextDatum("APPLY");
313  if ((blk->bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
314  flags[cnt++] = CStringGetTextDatum("COMPRESS_PGLZ");
315  if ((blk->bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
316  flags[cnt++] = CStringGetTextDatum("COMPRESS_LZ4");
317  if ((blk->bimg_info & BKPIMAGE_COMPRESS_ZSTD) != 0)
318  flags[cnt++] = CStringGetTextDatum("COMPRESS_ZSTD");
319 
320  Assert(cnt <= bitcnt);
321  block_fpi_info = construct_array_builtin(flags, cnt, TEXTOID);
322  }
323 
324  /* start_lsn, end_lsn, prev_lsn, and blockid outputs */
325  values[i++] = LSNGetDatum(record->ReadRecPtr);
326  values[i++] = LSNGetDatum(record->EndRecPtr);
327  values[i++] = LSNGetDatum(XLogRecGetPrev(record));
328  values[i++] = Int16GetDatum(block_id);
329 
330  /* relfile and block related outputs */
334  values[i++] = Int16GetDatum(forknum);
335  values[i++] = Int64GetDatum((int64) blkno);
336 
337  /* xid, resource_manager, and record_type outputs */
339  values[i++] = CStringGetTextDatum(desc.rm_name);
340  values[i++] = CStringGetTextDatum(record_type);
341 
342  /*
343  * record_length, main_data_length, block_data_len, and
344  * block_fpi_length outputs
345  */
348  values[i++] = UInt32GetDatum(block_data_len);
349  values[i++] = UInt32GetDatum(block_fpi_len);
350 
351  /* block_fpi_info (text array) output */
352  if (block_fpi_info)
353  values[i++] = PointerGetDatum(block_fpi_info);
354  else
355  nulls[i++] = true;
356 
357  /* description output (describes WAL record) */
358  if (rec_desc.len > 0)
359  values[i++] = CStringGetTextDatum(rec_desc.data);
360  else
361  nulls[i++] = true;
362 
363  /* block_data output */
364  if (blk->has_data && show_data)
365  {
366  bytea *block_data;
367 
368  block_data = (bytea *) palloc(block_data_len + VARHDRSZ);
369  SET_VARSIZE(block_data, block_data_len + VARHDRSZ);
370  memcpy(VARDATA(block_data), blk->data, block_data_len);
371  values[i++] = PointerGetDatum(block_data);
372  }
373  else
374  nulls[i++] = true;
375 
376  /* block_fpi_data output */
377  if (blk->has_image && show_data)
378  {
380  Page page;
381  bytea *block_fpi_data;
382 
383  page = (Page) buf.data;
384  if (!RestoreBlockImage(record, block_id, page))
385  ereport(ERROR,
386  (errcode(ERRCODE_INTERNAL_ERROR),
387  errmsg_internal("%s", record->errormsg_buf)));
388 
389  block_fpi_data = (bytea *) palloc(BLCKSZ + VARHDRSZ);
390  SET_VARSIZE(block_fpi_data, BLCKSZ + VARHDRSZ);
391  memcpy(VARDATA(block_fpi_data), page, BLCKSZ);
392  values[i++] = PointerGetDatum(block_fpi_data);
393  }
394  else
395  nulls[i++] = true;
396 
398 
399  /* Store a tuple for this block reference */
400  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
401  values, nulls);
402  }
403 
404 #undef PG_GET_WAL_BLOCK_INFO_COLS
405 }
406 
407 /*
408  * Get WAL record info, unnested by block reference
409  */
410 Datum
412 {
413  XLogRecPtr start_lsn = PG_GETARG_LSN(0);
414  XLogRecPtr end_lsn = PG_GETARG_LSN(1);
415  bool show_data = PG_GETARG_BOOL(2);
417  MemoryContext old_cxt;
418  MemoryContext tmp_cxt;
419 
420  ValidateInputLSNs(start_lsn, &end_lsn);
421 
422  InitMaterializedSRF(fcinfo, 0);
423 
424  xlogreader = InitXLogReaderState(start_lsn);
425 
427  "pg_get_wal_block_info temporary cxt",
429 
430  while (ReadNextXLogRecord(xlogreader) &&
431  xlogreader->EndRecPtr <= end_lsn)
432  {
434 
436  continue;
437 
438  /* Use the tmp context so we can clean up after each tuple is done */
439  old_cxt = MemoryContextSwitchTo(tmp_cxt);
440 
441  GetWALBlockInfo(fcinfo, xlogreader, show_data);
442 
443  /* clean up and switch back */
444  MemoryContextSwitchTo(old_cxt);
445  MemoryContextReset(tmp_cxt);
446  }
447 
448  MemoryContextDelete(tmp_cxt);
451 
452  PG_RETURN_VOID();
453 }
454 
455 /*
456  * Get WAL record info.
457  */
458 Datum
460 {
461 #define PG_GET_WAL_RECORD_INFO_COLS 11
462  Datum result;
464  bool nulls[PG_GET_WAL_RECORD_INFO_COLS] = {0};
465  XLogRecPtr lsn;
466  XLogRecPtr curr_lsn;
468  TupleDesc tupdesc;
469  HeapTuple tuple;
470 
471  lsn = PG_GETARG_LSN(0);
472  curr_lsn = GetCurrentLSN();
473 
474  if (lsn > curr_lsn)
475  ereport(ERROR,
476  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
477  errmsg("WAL input LSN must be less than current LSN"),
478  errdetail("Current WAL LSN on the database system is at %X/%X.",
479  LSN_FORMAT_ARGS(curr_lsn))));
480 
481  /* Build a tuple descriptor for our result type. */
482  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
483  elog(ERROR, "return type must be a row type");
484 
486 
488  ereport(ERROR,
489  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
490  errmsg("could not read WAL at %X/%X",
492 
494 
497 
498  tuple = heap_form_tuple(tupdesc, values, nulls);
499  result = HeapTupleGetDatum(tuple);
500 
501  PG_RETURN_DATUM(result);
502 #undef PG_GET_WAL_RECORD_INFO_COLS
503 }
504 
505 /*
506  * Validate start and end LSNs coming from the function inputs.
507  *
508  * If end_lsn is found to be higher than the current LSN reported by the
509  * cluster, use the current LSN as the upper bound.
510  */
511 static void
513 {
514  XLogRecPtr curr_lsn = GetCurrentLSN();
515 
516  if (start_lsn > curr_lsn)
517  ereport(ERROR,
518  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
519  errmsg("WAL start LSN must be less than current LSN"),
520  errdetail("Current WAL LSN on the database system is at %X/%X.",
521  LSN_FORMAT_ARGS(curr_lsn))));
522 
523  if (start_lsn > *end_lsn)
524  ereport(ERROR,
525  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
526  errmsg("WAL start LSN must be less than end LSN")));
527 
528  if (*end_lsn > curr_lsn)
529  *end_lsn = curr_lsn;
530 }
531 
532 /*
533  * Get info of all WAL records between start LSN and end LSN.
534  */
535 static void
537  XLogRecPtr end_lsn)
538 {
539 #define PG_GET_WAL_RECORDS_INFO_COLS 11
541  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
542  MemoryContext old_cxt;
543  MemoryContext tmp_cxt;
544 
545  Assert(start_lsn <= end_lsn);
546 
547  InitMaterializedSRF(fcinfo, 0);
548 
549  xlogreader = InitXLogReaderState(start_lsn);
550 
552  "GetWALRecordsInfo temporary cxt",
554 
555  while (ReadNextXLogRecord(xlogreader) &&
556  xlogreader->EndRecPtr <= end_lsn)
557  {
559  bool nulls[PG_GET_WAL_RECORDS_INFO_COLS] = {0};
560 
561  /* Use the tmp context so we can clean up after each tuple is done */
562  old_cxt = MemoryContextSwitchTo(tmp_cxt);
563 
566 
567  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
568  values, nulls);
569 
570  /* clean up and switch back */
571  MemoryContextSwitchTo(old_cxt);
572  MemoryContextReset(tmp_cxt);
573 
575  }
576 
577  MemoryContextDelete(tmp_cxt);
580 
581 #undef PG_GET_WAL_RECORDS_INFO_COLS
582 }
583 
584 /*
585  * Get info of all WAL records between start LSN and end LSN.
586  */
587 Datum
589 {
590  XLogRecPtr start_lsn = PG_GETARG_LSN(0);
591  XLogRecPtr end_lsn = PG_GETARG_LSN(1);
592 
593  ValidateInputLSNs(start_lsn, &end_lsn);
594  GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
595 
596  PG_RETURN_VOID();
597 }
598 
599 /*
600  * Fill single row of record counts and sizes for an rmgr or record.
601  */
602 static void
603 FillXLogStatsRow(const char *name,
604  uint64 n, uint64 total_count,
605  uint64 rec_len, uint64 total_rec_len,
606  uint64 fpi_len, uint64 total_fpi_len,
607  uint64 tot_len, uint64 total_len,
608  Datum *values, bool *nulls, uint32 ncols)
609 {
610  double n_pct,
611  rec_len_pct,
612  fpi_len_pct,
613  tot_len_pct;
614  int i = 0;
615 
616  n_pct = 0;
617  if (total_count != 0)
618  n_pct = 100 * (double) n / total_count;
619 
620  rec_len_pct = 0;
621  if (total_rec_len != 0)
622  rec_len_pct = 100 * (double) rec_len / total_rec_len;
623 
624  fpi_len_pct = 0;
625  if (total_fpi_len != 0)
626  fpi_len_pct = 100 * (double) fpi_len / total_fpi_len;
627 
628  tot_len_pct = 0;
629  if (total_len != 0)
630  tot_len_pct = 100 * (double) tot_len / total_len;
631 
633  values[i++] = Int64GetDatum(n);
634  values[i++] = Float8GetDatum(n_pct);
635  values[i++] = Int64GetDatum(rec_len);
636  values[i++] = Float8GetDatum(rec_len_pct);
637  values[i++] = Int64GetDatum(fpi_len);
638  values[i++] = Float8GetDatum(fpi_len_pct);
639  values[i++] = Int64GetDatum(tot_len);
640  values[i++] = Float8GetDatum(tot_len_pct);
641 
642  Assert(i == ncols);
643 }
644 
645 /*
646  * Get summary statistics about the records seen so far.
647  */
648 static void
650  Datum *values, bool *nulls, uint32 ncols,
651  bool stats_per_record)
652 {
653  MemoryContext old_cxt;
654  MemoryContext tmp_cxt;
655  uint64 total_count = 0;
656  uint64 total_rec_len = 0;
657  uint64 total_fpi_len = 0;
658  uint64 total_len = 0;
659  int ri;
660 
661  /*
662  * Each row shows its percentages of the total, so make a first pass to
663  * calculate column totals.
664  */
665  for (ri = 0; ri <= RM_MAX_ID; ri++)
666  {
667  if (!RmgrIdIsValid(ri))
668  continue;
669 
670  total_count += stats->rmgr_stats[ri].count;
671  total_rec_len += stats->rmgr_stats[ri].rec_len;
672  total_fpi_len += stats->rmgr_stats[ri].fpi_len;
673  }
674  total_len = total_rec_len + total_fpi_len;
675 
677  "GetXLogSummaryStats temporary cxt",
679 
680  for (ri = 0; ri <= RM_MAX_ID; ri++)
681  {
682  uint64 count;
683  uint64 rec_len;
684  uint64 fpi_len;
685  uint64 tot_len;
686  RmgrData desc;
687 
688  if (!RmgrIdIsValid(ri))
689  continue;
690 
691  if (!RmgrIdExists(ri))
692  continue;
693 
694  desc = GetRmgr(ri);
695 
696  if (stats_per_record)
697  {
698  int rj;
699 
700  for (rj = 0; rj < MAX_XLINFO_TYPES; rj++)
701  {
702  const char *id;
703 
704  count = stats->record_stats[ri][rj].count;
705  rec_len = stats->record_stats[ri][rj].rec_len;
706  fpi_len = stats->record_stats[ri][rj].fpi_len;
707  tot_len = rec_len + fpi_len;
708 
709  /* Skip undefined combinations and ones that didn't occur */
710  if (count == 0)
711  continue;
712 
713  old_cxt = MemoryContextSwitchTo(tmp_cxt);
714 
715  /* the upper four bits in xl_info are the rmgr's */
716  id = desc.rm_identify(rj << 4);
717  if (id == NULL)
718  id = psprintf("UNKNOWN (%x)", rj << 4);
719 
720  FillXLogStatsRow(psprintf("%s/%s", desc.rm_name, id), count,
721  total_count, rec_len, total_rec_len, fpi_len,
722  total_fpi_len, tot_len, total_len,
723  values, nulls, ncols);
724 
725  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
726  values, nulls);
727 
728  /* clean up and switch back */
729  MemoryContextSwitchTo(old_cxt);
730  MemoryContextReset(tmp_cxt);
731  }
732  }
733  else
734  {
735  count = stats->rmgr_stats[ri].count;
736  rec_len = stats->rmgr_stats[ri].rec_len;
737  fpi_len = stats->rmgr_stats[ri].fpi_len;
738  tot_len = rec_len + fpi_len;
739 
740  old_cxt = MemoryContextSwitchTo(tmp_cxt);
741 
742  FillXLogStatsRow(desc.rm_name, count, total_count, rec_len,
743  total_rec_len, fpi_len, total_fpi_len, tot_len,
744  total_len, values, nulls, ncols);
745 
746  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
747  values, nulls);
748 
749  /* clean up and switch back */
750  MemoryContextSwitchTo(old_cxt);
751  MemoryContextReset(tmp_cxt);
752  }
753  }
754 
755  MemoryContextDelete(tmp_cxt);
756 }
757 
758 /*
759  * Get WAL stats between start LSN and end LSN.
760  */
761 static void
763  bool stats_per_record)
764 {
765 #define PG_GET_WAL_STATS_COLS 9
767  XLogStats stats = {0};
768  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
770  bool nulls[PG_GET_WAL_STATS_COLS] = {0};
771 
772  Assert(start_lsn <= end_lsn);
773 
774  InitMaterializedSRF(fcinfo, 0);
775 
776  xlogreader = InitXLogReaderState(start_lsn);
777 
778  while (ReadNextXLogRecord(xlogreader) &&
779  xlogreader->EndRecPtr <= end_lsn)
780  {
781  XLogRecStoreStats(&stats, xlogreader);
782 
784  }
785 
788 
789  GetXLogSummaryStats(&stats, rsinfo, values, nulls,
791  stats_per_record);
792 
793 #undef PG_GET_WAL_STATS_COLS
794 }
795 
796 /*
797  * Get stats of all WAL records between start LSN and end LSN.
798  */
799 Datum
801 {
802  XLogRecPtr start_lsn = PG_GETARG_LSN(0);
803  XLogRecPtr end_lsn = PG_GETARG_LSN(1);
804  bool stats_per_record = PG_GETARG_BOOL(2);
805 
806  ValidateInputLSNs(start_lsn, &end_lsn);
807  GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
808 
809  PG_RETURN_VOID();
810 }
811 
812 /*
813  * The following functions have been removed in newer versions in 1.1, but
814  * they are kept around for compatibility.
815  */
816 Datum
818 {
819  XLogRecPtr start_lsn = PG_GETARG_LSN(0);
820  XLogRecPtr end_lsn = GetCurrentLSN();
821 
822  if (start_lsn > end_lsn)
823  ereport(ERROR,
824  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
825  errmsg("WAL start LSN must be less than current LSN"),
826  errdetail("Current WAL LSN on the database system is at %X/%X.",
827  LSN_FORMAT_ARGS(end_lsn))));
828 
829  GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
830 
831  PG_RETURN_VOID();
832 }
833 
834 Datum
836 {
837  XLogRecPtr start_lsn = PG_GETARG_LSN(0);
838  XLogRecPtr end_lsn = GetCurrentLSN();
839  bool stats_per_record = PG_GETARG_BOOL(1);
840 
841  if (start_lsn > end_lsn)
842  ereport(ERROR,
843  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
844  errmsg("WAL start LSN must be less than current LSN"),
845  errdetail("Current WAL LSN on the database system is at %X/%X.",
846  LSN_FORMAT_ARGS(end_lsn))));
847 
848  GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
849 
850  PG_RETURN_VOID();
851 }
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3374
uint32 BlockNumber
Definition: block.h:31
static Datum values[MAXATTR]
Definition: bootstrap.c:152
Pointer Page
Definition: bufpage.h:78
#define CStringGetTextDatum(s)
Definition: builtins.h:97
unsigned int uint32
Definition: c.h:506
#define VARHDRSZ
Definition: c.h:692
#define Assert(condition)
Definition: c.h:858
unsigned char uint8
Definition: c.h:504
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1157
int errcode_for_file_access(void)
Definition: elog.c:880
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:857
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
Datum Float8GetDatum(float8 X)
Definition: fmgr.c:1816
#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, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
void pfree(void *pointer)
Definition: mcxt.c:1520
void * palloc0(Size size)
Definition: mcxt.c:1346
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
void * palloc(Size size)
Definition: mcxt.c:1316
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
static uint64 pg_popcount(const char *buf, int bytes)
Definition: pg_bitutils.h:339
#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:73
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
#define PG_GET_WAL_RECORD_INFO_COLS
static void GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record, bool show_data)
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:91
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:69
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
MemoryContextSwitchTo(old_ctx)
ForkNumber
Definition: relpath.h:48
#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:340
Tuplestorestate * setResult
Definition: execnodes.h:339
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:687
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition: tuplestore.c:750
#define VARDATA(PTR)
Definition: varatt.h:278
#define SET_VARSIZE(PTR, len)
Definition: varatt.h:305
const char * name
bool RecoveryInProgress(void)
Definition: xlog.c:6290
int wal_segment_size
Definition: xlog.c:143
XLogRecPtr GetFlushRecPtr(TimeLineID *insertTLI)
Definition: xlog.c:6455
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:216
bool XLogRecGetBlockTagExtended(XLogReaderState *record, uint8 block_id, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum, Buffer *prefetch_buffer)
Definition: xlogreader.c:1997
XLogRecord * XLogReadRecord(XLogReaderState *state, char **errormsg)
Definition: xlogreader.c:389
void XLogReaderFree(XLogReaderState *state)
Definition: xlogreader.c:161
XLogReaderState * XLogReaderAllocate(int wal_segment_size, const char *waldir, XLogReaderRoutine *routine, void *private_data)
Definition: xlogreader.c:106
XLogRecPtr XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr)
Definition: xlogreader.c:1383
bool RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
Definition: xlogreader.c:2056
#define XLogRecGetDataLen(decoder)
Definition: xlogreader.h:416
#define XLogRecGetInfo(decoder)
Definition: xlogreader.h:410
#define XLogRecGetRmid(decoder)
Definition: xlogreader.h:411
#define XLogRecGetTotalLen(decoder)
Definition: xlogreader.h:408
#define XLogRecGetXid(decoder)
Definition: xlogreader.h:412
#define XLogRecGetBlock(decoder, i)
Definition: xlogreader.h:419
#define XL_ROUTINE(...)
Definition: xlogreader.h:117
#define XLogRecMaxBlockId(decoder)
Definition: xlogreader.h:418
#define XLogRecHasBlockRef(decoder, block_id)
Definition: xlogreader.h:420
#define XLogRecGetPrev(decoder)
Definition: xlogreader.h:409
#define XLogRecHasAnyBlockRefs(decoder)
Definition: xlogreader.h:417
#define BKPIMAGE_COMPRESS_ZSTD
Definition: xlogrecord.h:162
#define BKPIMAGE_HAS_HOLE
Definition: xlogrecord.h:157
#define BKPIMAGE_COMPRESS_LZ4
Definition: xlogrecord.h:161
#define XLR_INFO_MASK
Definition: xlogrecord.h:62
#define BKPIMAGE_COMPRESS_PGLZ
Definition: xlogrecord.h:160
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:842
void wal_segment_open(XLogReaderState *state, XLogSegNo nextSegNo, TimeLineID *tli_p)
Definition: xlogutils.c:817
int read_local_xlog_page_no_wait(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
Definition: xlogutils.c:873