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