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