pg_amcheck: Fix test failure on Windows with non-existing role
[pgsql.git] / contrib / pg_walinspect / pg_walinspect.c
blob9e6094157894a9629af4f6147add54244c7729cf
1 /*-------------------------------------------------------------------------
3 * pg_walinspect.c
4 * Functions to inspect contents of PostgreSQL Write-Ahead Log
6 * Copyright (c) 2022-2025, PostgreSQL Global Development Group
8 * IDENTIFICATION
9 * contrib/pg_walinspect/pg_walinspect.c
11 *-------------------------------------------------------------------------
13 #include "postgres.h"
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"
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.
32 PG_MODULE_MAGIC;
34 PG_FUNCTION_INFO_V1(pg_get_wal_block_info);
35 PG_FUNCTION_INFO_V1(pg_get_wal_record_info);
36 PG_FUNCTION_INFO_V1(pg_get_wal_records_info);
37 PG_FUNCTION_INFO_V1(pg_get_wal_records_info_till_end_of_wal);
38 PG_FUNCTION_INFO_V1(pg_get_wal_stats);
39 PG_FUNCTION_INFO_V1(pg_get_wal_stats_till_end_of_wal);
41 static void ValidateInputLSNs(XLogRecPtr start_lsn, XLogRecPtr *end_lsn);
42 static XLogRecPtr GetCurrentLSN(void);
43 static XLogReaderState *InitXLogReaderState(XLogRecPtr lsn);
44 static XLogRecord *ReadNextXLogRecord(XLogReaderState *xlogreader);
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);
66 * Return the LSN up to which the server has WAL.
68 static XLogRecPtr
69 GetCurrentLSN(void)
71 XLogRecPtr curr_lsn;
74 * We determine the current LSN of the server similar to how page_read
75 * callback read_local_xlog_page_no_wait does.
77 if (!RecoveryInProgress())
78 curr_lsn = GetFlushRecPtr(NULL);
79 else
80 curr_lsn = GetXLogReplayRecPtr(NULL);
82 Assert(!XLogRecPtrIsInvalid(curr_lsn));
84 return curr_lsn;
88 * Initialize WAL reader and identify first valid LSN.
90 static XLogReaderState *
91 InitXLogReaderState(XLogRecPtr lsn)
93 XLogReaderState *xlogreader;
94 ReadLocalXLogPageNoWaitPrivate *private_data;
95 XLogRecPtr first_valid_record;
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.
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))));
108 private_data = (ReadLocalXLogPageNoWaitPrivate *)
109 palloc0(sizeof(ReadLocalXLogPageNoWaitPrivate));
111 xlogreader = XLogReaderAllocate(wal_segment_size, NULL,
112 XL_ROUTINE(.page_read = &read_local_xlog_page_no_wait,
113 .segment_open = &wal_segment_open,
114 .segment_close = &wal_segment_close),
115 private_data);
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.")));
123 /* first find a valid recptr to start from */
124 first_valid_record = XLogFindNextRecord(xlogreader, lsn);
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))));
131 return xlogreader;
135 * Read next WAL record.
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.
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.
146 static XLogRecord *
147 ReadNextXLogRecord(XLogReaderState *xlogreader)
149 XLogRecord *record;
150 char *errormsg;
152 record = XLogReadRecord(xlogreader, &errormsg);
154 if (record == NULL)
156 ReadLocalXLogPageNoWaitPrivate *private_data;
158 /* return NULL, if end of WAL is reached */
159 private_data = (ReadLocalXLogPageNoWaitPrivate *)
160 xlogreader->private_data;
162 if (private_data->end_of_wal)
163 return NULL;
165 if (errormsg)
166 ereport(ERROR,
167 (errcode_for_file_access(),
168 errmsg("could not read WAL at %X/%X: %s",
169 LSN_FORMAT_ARGS(xlogreader->EndRecPtr), errormsg)));
170 else
171 ereport(ERROR,
172 (errcode_for_file_access(),
173 errmsg("could not read WAL at %X/%X",
174 LSN_FORMAT_ARGS(xlogreader->EndRecPtr))));
177 return record;
181 * Output values that make up a row describing caller's WAL record.
183 * This function leaks memory. Caller may need to use its own custom memory
184 * context.
186 * Keep this in sync with GetWALBlockInfo.
188 static void
189 GetWALRecordInfo(XLogReaderState *record, Datum *values,
190 bool *nulls, uint32 ncols)
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;
199 desc = GetRmgr(XLogRecGetRmid(record));
200 record_type = desc.rm_identify(XLogRecGetInfo(record));
202 if (record_type == NULL)
203 record_type = psprintf("UNKNOWN (%x)", XLogRecGetInfo(record) & ~XLR_INFO_MASK);
205 initStringInfo(&rec_desc);
206 desc.rm_desc(&rec_desc, record);
208 if (XLogRecHasAnyBlockRefs(record))
210 initStringInfo(&rec_blk_ref);
211 XLogRecGetBlockRefInfo(record, false, true, &rec_blk_ref, &fpi_len);
214 values[i++] = LSNGetDatum(record->ReadRecPtr);
215 values[i++] = LSNGetDatum(record->EndRecPtr);
216 values[i++] = LSNGetDatum(XLogRecGetPrev(record));
217 values[i++] = TransactionIdGetDatum(XLogRecGetXid(record));
218 values[i++] = CStringGetTextDatum(desc.rm_name);
219 values[i++] = CStringGetTextDatum(record_type);
220 values[i++] = UInt32GetDatum(XLogRecGetTotalLen(record));
221 values[i++] = UInt32GetDatum(XLogRecGetDataLen(record));
222 values[i++] = UInt32GetDatum(fpi_len);
224 if (rec_desc.len > 0)
225 values[i++] = CStringGetTextDatum(rec_desc.data);
226 else
227 nulls[i++] = true;
229 if (XLogRecHasAnyBlockRefs(record))
230 values[i++] = CStringGetTextDatum(rec_blk_ref.data);
231 else
232 nulls[i++] = true;
234 Assert(i == ncols);
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.)
243 * This function leaks memory. Caller may need to use its own custom memory
244 * context.
246 * Keep this in sync with GetWALRecordInfo.
248 static void
249 GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record,
250 bool show_data)
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;
259 Assert(XLogRecHasAnyBlockRefs(record));
261 desc = GetRmgr(XLogRecGetRmid(record));
262 record_type = desc.rm_identify(XLogRecGetInfo(record));
264 if (record_type == NULL)
265 record_type = psprintf("UNKNOWN (%x)",
266 XLogRecGetInfo(record) & ~XLR_INFO_MASK);
268 initStringInfo(&rec_desc);
269 desc.rm_desc(&rec_desc, record);
271 for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
273 DecodedBkpBlock *blk;
274 BlockNumber blkno;
275 RelFileLocator rnode;
276 ForkNumber forknum;
277 Datum values[PG_GET_WAL_BLOCK_INFO_COLS] = {0};
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;
284 if (!XLogRecHasBlockRef(record, block_id))
285 continue;
287 blk = XLogRecGetBlock(record, block_id);
289 (void) XLogRecGetBlockTagExtended(record, block_id,
290 &rnode, &forknum, &blkno, NULL);
292 /* Save block_data_len */
293 if (blk->has_data)
294 block_data_len = blk->data_len;
296 if (blk->has_image)
298 /* Block reference has an FPI, so prepare relevant output */
299 int bitcnt;
300 int cnt = 0;
301 Datum *flags;
303 /* Save block_fpi_len */
304 block_fpi_len = blk->bimg_len;
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");
321 Assert(cnt <= bitcnt);
322 block_fpi_info = construct_array_builtin(flags, cnt, TEXTOID);
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);
331 /* relfile and block related outputs */
332 values[i++] = ObjectIdGetDatum(blk->rlocator.spcOid);
333 values[i++] = ObjectIdGetDatum(blk->rlocator.dbOid);
334 values[i++] = ObjectIdGetDatum(blk->rlocator.relNumber);
335 values[i++] = Int16GetDatum(forknum);
336 values[i++] = Int64GetDatum((int64) blkno);
338 /* xid, resource_manager, and record_type outputs */
339 values[i++] = TransactionIdGetDatum(XLogRecGetXid(record));
340 values[i++] = CStringGetTextDatum(desc.rm_name);
341 values[i++] = CStringGetTextDatum(record_type);
344 * record_length, main_data_length, block_data_len, and
345 * block_fpi_length outputs
347 values[i++] = UInt32GetDatum(XLogRecGetTotalLen(record));
348 values[i++] = UInt32GetDatum(XLogRecGetDataLen(record));
349 values[i++] = UInt32GetDatum(block_data_len);
350 values[i++] = UInt32GetDatum(block_fpi_len);
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;
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;
364 /* block_data output */
365 if (blk->has_data && show_data)
367 bytea *block_data;
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);
374 else
375 nulls[i++] = true;
377 /* block_fpi_data output */
378 if (blk->has_image && show_data)
380 PGAlignedBlock buf;
381 Page page;
382 bytea *block_fpi_data;
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)));
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);
395 else
396 nulls[i++] = true;
398 Assert(i == PG_GET_WAL_BLOCK_INFO_COLS);
400 /* Store a tuple for this block reference */
401 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
402 values, nulls);
405 #undef PG_GET_WAL_BLOCK_INFO_COLS
409 * Get WAL record info, unnested by block reference
411 Datum
412 pg_get_wal_block_info(PG_FUNCTION_ARGS)
414 XLogRecPtr start_lsn = PG_GETARG_LSN(0);
415 XLogRecPtr end_lsn = PG_GETARG_LSN(1);
416 bool show_data = PG_GETARG_BOOL(2);
417 XLogReaderState *xlogreader;
418 MemoryContext old_cxt;
419 MemoryContext tmp_cxt;
421 ValidateInputLSNs(start_lsn, &end_lsn);
423 InitMaterializedSRF(fcinfo, 0);
425 xlogreader = InitXLogReaderState(start_lsn);
427 tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
428 "pg_get_wal_block_info temporary cxt",
429 ALLOCSET_DEFAULT_SIZES);
431 while (ReadNextXLogRecord(xlogreader) &&
432 xlogreader->EndRecPtr <= end_lsn)
434 CHECK_FOR_INTERRUPTS();
436 if (!XLogRecHasAnyBlockRefs(xlogreader))
437 continue;
439 /* Use the tmp context so we can clean up after each tuple is done */
440 old_cxt = MemoryContextSwitchTo(tmp_cxt);
442 GetWALBlockInfo(fcinfo, xlogreader, show_data);
444 /* clean up and switch back */
445 MemoryContextSwitchTo(old_cxt);
446 MemoryContextReset(tmp_cxt);
449 MemoryContextDelete(tmp_cxt);
450 pfree(xlogreader->private_data);
451 XLogReaderFree(xlogreader);
453 PG_RETURN_VOID();
457 * Get WAL record info.
459 Datum
460 pg_get_wal_record_info(PG_FUNCTION_ARGS)
462 #define PG_GET_WAL_RECORD_INFO_COLS 11
463 Datum result;
464 Datum values[PG_GET_WAL_RECORD_INFO_COLS] = {0};
465 bool nulls[PG_GET_WAL_RECORD_INFO_COLS] = {0};
466 XLogRecPtr lsn;
467 XLogRecPtr curr_lsn;
468 XLogReaderState *xlogreader;
469 TupleDesc tupdesc;
470 HeapTuple tuple;
472 lsn = PG_GETARG_LSN(0);
473 curr_lsn = GetCurrentLSN();
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))));
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");
486 xlogreader = InitXLogReaderState(lsn);
488 if (!ReadNextXLogRecord(xlogreader))
489 ereport(ERROR,
490 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
491 errmsg("could not read WAL at %X/%X",
492 LSN_FORMAT_ARGS(xlogreader->EndRecPtr))));
494 GetWALRecordInfo(xlogreader, values, nulls, PG_GET_WAL_RECORD_INFO_COLS);
496 pfree(xlogreader->private_data);
497 XLogReaderFree(xlogreader);
499 tuple = heap_form_tuple(tupdesc, values, nulls);
500 result = HeapTupleGetDatum(tuple);
502 PG_RETURN_DATUM(result);
503 #undef PG_GET_WAL_RECORD_INFO_COLS
507 * Validate start and end LSNs coming from the function inputs.
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.
512 static void
513 ValidateInputLSNs(XLogRecPtr start_lsn, XLogRecPtr *end_lsn)
515 XLogRecPtr curr_lsn = GetCurrentLSN();
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))));
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")));
529 if (*end_lsn > curr_lsn)
530 *end_lsn = curr_lsn;
534 * Get info of all WAL records between start LSN and end LSN.
536 static void
537 GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
538 XLogRecPtr end_lsn)
540 #define PG_GET_WAL_RECORDS_INFO_COLS 11
541 XLogReaderState *xlogreader;
542 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
543 MemoryContext old_cxt;
544 MemoryContext tmp_cxt;
546 Assert(start_lsn <= end_lsn);
548 InitMaterializedSRF(fcinfo, 0);
550 xlogreader = InitXLogReaderState(start_lsn);
552 tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
553 "GetWALRecordsInfo temporary cxt",
554 ALLOCSET_DEFAULT_SIZES);
556 while (ReadNextXLogRecord(xlogreader) &&
557 xlogreader->EndRecPtr <= end_lsn)
559 Datum values[PG_GET_WAL_RECORDS_INFO_COLS] = {0};
560 bool nulls[PG_GET_WAL_RECORDS_INFO_COLS] = {0};
562 /* Use the tmp context so we can clean up after each tuple is done */
563 old_cxt = MemoryContextSwitchTo(tmp_cxt);
565 GetWALRecordInfo(xlogreader, values, nulls,
566 PG_GET_WAL_RECORDS_INFO_COLS);
568 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
569 values, nulls);
571 /* clean up and switch back */
572 MemoryContextSwitchTo(old_cxt);
573 MemoryContextReset(tmp_cxt);
575 CHECK_FOR_INTERRUPTS();
578 MemoryContextDelete(tmp_cxt);
579 pfree(xlogreader->private_data);
580 XLogReaderFree(xlogreader);
582 #undef PG_GET_WAL_RECORDS_INFO_COLS
586 * Get info of all WAL records between start LSN and end LSN.
588 Datum
589 pg_get_wal_records_info(PG_FUNCTION_ARGS)
591 XLogRecPtr start_lsn = PG_GETARG_LSN(0);
592 XLogRecPtr end_lsn = PG_GETARG_LSN(1);
594 ValidateInputLSNs(start_lsn, &end_lsn);
595 GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
597 PG_RETURN_VOID();
601 * Fill single row of record counts and sizes for an rmgr or record.
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)
611 double n_pct,
612 rec_len_pct,
613 fpi_len_pct,
614 tot_len_pct;
615 int i = 0;
617 n_pct = 0;
618 if (total_count != 0)
619 n_pct = 100 * (double) n / total_count;
621 rec_len_pct = 0;
622 if (total_rec_len != 0)
623 rec_len_pct = 100 * (double) rec_len / total_rec_len;
625 fpi_len_pct = 0;
626 if (total_fpi_len != 0)
627 fpi_len_pct = 100 * (double) fpi_len / total_fpi_len;
629 tot_len_pct = 0;
630 if (total_len != 0)
631 tot_len_pct = 100 * (double) tot_len / total_len;
633 values[i++] = CStringGetTextDatum(name);
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);
643 Assert(i == ncols);
647 * Get summary statistics about the records seen so far.
649 static void
650 GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
651 Datum *values, bool *nulls, uint32 ncols,
652 bool stats_per_record)
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;
663 * Each row shows its percentages of the total, so make a first pass to
664 * calculate column totals.
666 for (ri = 0; ri <= RM_MAX_ID; ri++)
668 if (!RmgrIdIsValid(ri))
669 continue;
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;
675 total_len = total_rec_len + total_fpi_len;
677 tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
678 "GetXLogSummaryStats temporary cxt",
679 ALLOCSET_DEFAULT_SIZES);
681 for (ri = 0; ri <= RM_MAX_ID; ri++)
683 uint64 count;
684 uint64 rec_len;
685 uint64 fpi_len;
686 uint64 tot_len;
687 RmgrData desc;
689 if (!RmgrIdIsValid(ri))
690 continue;
692 if (!RmgrIdExists(ri))
693 continue;
695 desc = GetRmgr(ri);
697 if (stats_per_record)
699 int rj;
701 for (rj = 0; rj < MAX_XLINFO_TYPES; rj++)
703 const char *id;
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;
710 /* Skip undefined combinations and ones that didn't occur */
711 if (count == 0)
712 continue;
714 old_cxt = MemoryContextSwitchTo(tmp_cxt);
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);
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);
726 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
727 values, nulls);
729 /* clean up and switch back */
730 MemoryContextSwitchTo(old_cxt);
731 MemoryContextReset(tmp_cxt);
734 else
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;
741 old_cxt = MemoryContextSwitchTo(tmp_cxt);
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);
747 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
748 values, nulls);
750 /* clean up and switch back */
751 MemoryContextSwitchTo(old_cxt);
752 MemoryContextReset(tmp_cxt);
756 MemoryContextDelete(tmp_cxt);
760 * Get WAL stats between start LSN and end LSN.
762 static void
763 GetWalStats(FunctionCallInfo fcinfo, XLogRecPtr start_lsn, XLogRecPtr end_lsn,
764 bool stats_per_record)
766 #define PG_GET_WAL_STATS_COLS 9
767 XLogReaderState *xlogreader;
768 XLogStats stats = {0};
769 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
770 Datum values[PG_GET_WAL_STATS_COLS] = {0};
771 bool nulls[PG_GET_WAL_STATS_COLS] = {0};
773 Assert(start_lsn <= end_lsn);
775 InitMaterializedSRF(fcinfo, 0);
777 xlogreader = InitXLogReaderState(start_lsn);
779 while (ReadNextXLogRecord(xlogreader) &&
780 xlogreader->EndRecPtr <= end_lsn)
782 XLogRecStoreStats(&stats, xlogreader);
784 CHECK_FOR_INTERRUPTS();
787 pfree(xlogreader->private_data);
788 XLogReaderFree(xlogreader);
790 GetXLogSummaryStats(&stats, rsinfo, values, nulls,
791 PG_GET_WAL_STATS_COLS,
792 stats_per_record);
794 #undef PG_GET_WAL_STATS_COLS
798 * Get stats of all WAL records between start LSN and end LSN.
800 Datum
801 pg_get_wal_stats(PG_FUNCTION_ARGS)
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);
807 ValidateInputLSNs(start_lsn, &end_lsn);
808 GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
810 PG_RETURN_VOID();
814 * The following functions have been removed in newer versions in 1.1, but
815 * they are kept around for compatibility.
817 Datum
818 pg_get_wal_records_info_till_end_of_wal(PG_FUNCTION_ARGS)
820 XLogRecPtr start_lsn = PG_GETARG_LSN(0);
821 XLogRecPtr end_lsn = GetCurrentLSN();
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))));
830 GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
832 PG_RETURN_VOID();
835 Datum
836 pg_get_wal_stats_till_end_of_wal(PG_FUNCTION_ARGS)
838 XLogRecPtr start_lsn = PG_GETARG_LSN(0);
839 XLogRecPtr end_lsn = GetCurrentLSN();
840 bool stats_per_record = PG_GETARG_BOOL(1);
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))));
849 GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
851 PG_RETURN_VOID();