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