LCOV - code coverage report
Current view: top level - contrib/pg_walinspect - pg_walinspect.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 408 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 23 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * pg_walinspect.c
       4              :  *                Functions to inspect contents of PostgreSQL Write-Ahead Log
       5              :  *
       6              :  * Copyright (c) 2022-2026, 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"
      17              : #include "access/xlog_internal.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              : 
      33            0 : PG_MODULE_MAGIC_EXT(
      34              :                                         .name = "pg_walinspect",
      35              :                                         .version = PG_VERSION
      36              : );
      37              : 
      38            0 : PG_FUNCTION_INFO_V1(pg_get_wal_block_info);
      39            0 : PG_FUNCTION_INFO_V1(pg_get_wal_record_info);
      40            0 : PG_FUNCTION_INFO_V1(pg_get_wal_records_info);
      41            0 : PG_FUNCTION_INFO_V1(pg_get_wal_records_info_till_end_of_wal);
      42            0 : PG_FUNCTION_INFO_V1(pg_get_wal_stats);
      43            0 : PG_FUNCTION_INFO_V1(pg_get_wal_stats_till_end_of_wal);
      44              : 
      45              : static void ValidateInputLSNs(XLogRecPtr start_lsn, XLogRecPtr *end_lsn);
      46              : static XLogRecPtr GetCurrentLSN(void);
      47              : static XLogReaderState *InitXLogReaderState(XLogRecPtr lsn);
      48              : static XLogRecord *ReadNextXLogRecord(XLogReaderState *xlogreader);
      49              : static void GetWALRecordInfo(XLogReaderState *record, Datum *values,
      50              :                                                          bool *nulls, uint32 ncols);
      51              : static void GetWALRecordsInfo(FunctionCallInfo fcinfo,
      52              :                                                           XLogRecPtr start_lsn,
      53              :                                                           XLogRecPtr end_lsn);
      54              : static void GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
      55              :                                                                 Datum *values, bool *nulls, uint32 ncols,
      56              :                                                                 bool stats_per_record);
      57              : static 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);
      62              : static void GetWalStats(FunctionCallInfo fcinfo,
      63              :                                                 XLogRecPtr start_lsn,
      64              :                                                 XLogRecPtr end_lsn,
      65              :                                                 bool stats_per_record);
      66              : static void GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record,
      67              :                                                         bool show_data);
      68              : 
      69              : /*
      70              :  * Return the LSN up to which the server has WAL.
      71              :  */
      72              : static XLogRecPtr
      73            0 : GetCurrentLSN(void)
      74              : {
      75            0 :         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            0 :         if (!RecoveryInProgress())
      82            0 :                 curr_lsn = GetFlushRecPtr(NULL);
      83              :         else
      84            0 :                 curr_lsn = GetXLogReplayRecPtr(NULL);
      85              : 
      86            0 :         Assert(XLogRecPtrIsValid(curr_lsn));
      87              : 
      88            0 :         return curr_lsn;
      89            0 : }
      90              : 
      91              : /*
      92              :  * Initialize WAL reader and identify first valid LSN.
      93              :  */
      94              : static XLogReaderState *
      95            0 : InitXLogReaderState(XLogRecPtr lsn)
      96              : {
      97            0 :         XLogReaderState *xlogreader;
      98            0 :         ReadLocalXLogPageNoWaitPrivate *private_data;
      99            0 :         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            0 :         if (lsn < XLOG_BLCKSZ)
     107            0 :                 ereport(ERROR,
     108              :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     109              :                                  errmsg("could not read WAL at LSN %X/%08X",
     110              :                                                 LSN_FORMAT_ARGS(lsn))));
     111              : 
     112            0 :         private_data = palloc0_object(ReadLocalXLogPageNoWaitPrivate);
     113              : 
     114            0 :         xlogreader = XLogReaderAllocate(wal_segment_size, NULL,
     115            0 :                                                                         XL_ROUTINE(.page_read = &read_local_xlog_page_no_wait,
     116              :                                                                                            .segment_open = &wal_segment_open,
     117              :                                                                                            .segment_close = &wal_segment_close),
     118            0 :                                                                         private_data);
     119              : 
     120            0 :         if (xlogreader == NULL)
     121            0 :                 ereport(ERROR,
     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            0 :         first_valid_record = XLogFindNextRecord(xlogreader, lsn);
     128              : 
     129            0 :         if (!XLogRecPtrIsValid(first_valid_record))
     130            0 :                 ereport(ERROR,
     131              :                                 errmsg("could not find a valid record after %X/%08X",
     132              :                                            LSN_FORMAT_ARGS(lsn)));
     133              : 
     134            0 :         return xlogreader;
     135            0 : }
     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              :  */
     149              : static XLogRecord *
     150            0 : ReadNextXLogRecord(XLogReaderState *xlogreader)
     151              : {
     152            0 :         XLogRecord *record;
     153            0 :         char       *errormsg;
     154              : 
     155            0 :         record = XLogReadRecord(xlogreader, &errormsg);
     156              : 
     157            0 :         if (record == NULL)
     158              :         {
     159            0 :                 ReadLocalXLogPageNoWaitPrivate *private_data;
     160              : 
     161              :                 /* return NULL, if end of WAL is reached */
     162            0 :                 private_data = (ReadLocalXLogPageNoWaitPrivate *)
     163            0 :                         xlogreader->private_data;
     164              : 
     165            0 :                 if (private_data->end_of_wal)
     166            0 :                         return NULL;
     167              : 
     168            0 :                 if (errormsg)
     169            0 :                         ereport(ERROR,
     170              :                                         (errcode_for_file_access(),
     171              :                                          errmsg("could not read WAL at %X/%08X: %s",
     172              :                                                         LSN_FORMAT_ARGS(xlogreader->EndRecPtr), errormsg)));
     173              :                 else
     174            0 :                         ereport(ERROR,
     175              :                                         (errcode_for_file_access(),
     176              :                                          errmsg("could not read WAL at %X/%08X",
     177              :                                                         LSN_FORMAT_ARGS(xlogreader->EndRecPtr))));
     178            0 :         }
     179              : 
     180            0 :         return record;
     181            0 : }
     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              :  */
     191              : static void
     192            0 : GetWALRecordInfo(XLogReaderState *record, Datum *values,
     193              :                                  bool *nulls, uint32 ncols)
     194              : {
     195            0 :         const char *record_type;
     196            0 :         RmgrData        desc;
     197            0 :         uint32          fpi_len = 0;
     198            0 :         StringInfoData rec_desc;
     199            0 :         StringInfoData rec_blk_ref;
     200            0 :         int                     i = 0;
     201              : 
     202            0 :         desc = GetRmgr(XLogRecGetRmid(record));
     203            0 :         record_type = desc.rm_identify(XLogRecGetInfo(record));
     204              : 
     205            0 :         if (record_type == NULL)
     206            0 :                 record_type = psprintf("UNKNOWN (%x)", XLogRecGetInfo(record) & ~XLR_INFO_MASK);
     207              : 
     208            0 :         initStringInfo(&rec_desc);
     209            0 :         desc.rm_desc(&rec_desc, record);
     210              : 
     211            0 :         if (XLogRecHasAnyBlockRefs(record))
     212              :         {
     213            0 :                 initStringInfo(&rec_blk_ref);
     214            0 :                 XLogRecGetBlockRefInfo(record, false, true, &rec_blk_ref, &fpi_len);
     215            0 :         }
     216              : 
     217            0 :         values[i++] = LSNGetDatum(record->ReadRecPtr);
     218            0 :         values[i++] = LSNGetDatum(record->EndRecPtr);
     219            0 :         values[i++] = LSNGetDatum(XLogRecGetPrev(record));
     220            0 :         values[i++] = TransactionIdGetDatum(XLogRecGetXid(record));
     221            0 :         values[i++] = CStringGetTextDatum(desc.rm_name);
     222            0 :         values[i++] = CStringGetTextDatum(record_type);
     223            0 :         values[i++] = UInt32GetDatum(XLogRecGetTotalLen(record));
     224            0 :         values[i++] = UInt32GetDatum(XLogRecGetDataLen(record));
     225            0 :         values[i++] = UInt32GetDatum(fpi_len);
     226              : 
     227            0 :         if (rec_desc.len > 0)
     228            0 :                 values[i++] = CStringGetTextDatum(rec_desc.data);
     229              :         else
     230            0 :                 nulls[i++] = true;
     231              : 
     232            0 :         if (XLogRecHasAnyBlockRefs(record))
     233            0 :                 values[i++] = CStringGetTextDatum(rec_blk_ref.data);
     234              :         else
     235            0 :                 nulls[i++] = true;
     236              : 
     237            0 :         Assert(i == ncols);
     238            0 : }
     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              :  */
     251              : static void
     252            0 : GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record,
     253              :                                 bool show_data)
     254              : {
     255              : #define PG_GET_WAL_BLOCK_INFO_COLS 20
     256            0 :         int                     block_id;
     257            0 :         ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     258            0 :         RmgrData        desc;
     259            0 :         const char *record_type;
     260            0 :         StringInfoData rec_desc;
     261              : 
     262            0 :         Assert(XLogRecHasAnyBlockRefs(record));
     263              : 
     264            0 :         desc = GetRmgr(XLogRecGetRmid(record));
     265            0 :         record_type = desc.rm_identify(XLogRecGetInfo(record));
     266              : 
     267            0 :         if (record_type == NULL)
     268            0 :                 record_type = psprintf("UNKNOWN (%x)",
     269            0 :                                                            XLogRecGetInfo(record) & ~XLR_INFO_MASK);
     270              : 
     271            0 :         initStringInfo(&rec_desc);
     272            0 :         desc.rm_desc(&rec_desc, record);
     273              : 
     274            0 :         for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
     275              :         {
     276            0 :                 DecodedBkpBlock *blk;
     277            0 :                 BlockNumber blkno;
     278            0 :                 RelFileLocator rnode;
     279            0 :                 ForkNumber      forknum;
     280            0 :                 Datum           values[PG_GET_WAL_BLOCK_INFO_COLS] = {0};
     281            0 :                 bool            nulls[PG_GET_WAL_BLOCK_INFO_COLS] = {0};
     282            0 :                 uint32          block_data_len = 0,
     283            0 :                                         block_fpi_len = 0;
     284            0 :                 ArrayType  *block_fpi_info = NULL;
     285            0 :                 int                     i = 0;
     286              : 
     287            0 :                 if (!XLogRecHasBlockRef(record, block_id))
     288            0 :                         continue;
     289              : 
     290            0 :                 blk = XLogRecGetBlock(record, block_id);
     291              : 
     292            0 :                 (void) XLogRecGetBlockTagExtended(record, block_id,
     293              :                                                                                   &rnode, &forknum, &blkno, NULL);
     294              : 
     295              :                 /* Save block_data_len */
     296            0 :                 if (blk->has_data)
     297            0 :                         block_data_len = blk->data_len;
     298              : 
     299            0 :                 if (blk->has_image)
     300              :                 {
     301              :                         /* Block reference has an FPI, so prepare relevant output */
     302            0 :                         int                     bitcnt;
     303            0 :                         int                     cnt = 0;
     304            0 :                         Datum      *flags;
     305              : 
     306              :                         /* Save block_fpi_len */
     307            0 :                         block_fpi_len = blk->bimg_len;
     308              : 
     309              :                         /* Construct and save block_fpi_info */
     310            0 :                         bitcnt = pg_popcount((const char *) &blk->bimg_info,
     311              :                                                                  sizeof(uint8));
     312            0 :                         flags = palloc0_array(Datum, bitcnt);
     313            0 :                         if ((blk->bimg_info & BKPIMAGE_HAS_HOLE) != 0)
     314            0 :                                 flags[cnt++] = CStringGetTextDatum("HAS_HOLE");
     315            0 :                         if (blk->apply_image)
     316            0 :                                 flags[cnt++] = CStringGetTextDatum("APPLY");
     317            0 :                         if ((blk->bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
     318            0 :                                 flags[cnt++] = CStringGetTextDatum("COMPRESS_PGLZ");
     319            0 :                         if ((blk->bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
     320            0 :                                 flags[cnt++] = CStringGetTextDatum("COMPRESS_LZ4");
     321            0 :                         if ((blk->bimg_info & BKPIMAGE_COMPRESS_ZSTD) != 0)
     322            0 :                                 flags[cnt++] = CStringGetTextDatum("COMPRESS_ZSTD");
     323              : 
     324            0 :                         Assert(cnt <= bitcnt);
     325            0 :                         block_fpi_info = construct_array_builtin(flags, cnt, TEXTOID);
     326            0 :                 }
     327              : 
     328              :                 /* start_lsn, end_lsn, prev_lsn, and blockid outputs */
     329            0 :                 values[i++] = LSNGetDatum(record->ReadRecPtr);
     330            0 :                 values[i++] = LSNGetDatum(record->EndRecPtr);
     331            0 :                 values[i++] = LSNGetDatum(XLogRecGetPrev(record));
     332            0 :                 values[i++] = Int16GetDatum(block_id);
     333              : 
     334              :                 /* relfile and block related outputs */
     335            0 :                 values[i++] = ObjectIdGetDatum(blk->rlocator.spcOid);
     336            0 :                 values[i++] = ObjectIdGetDatum(blk->rlocator.dbOid);
     337            0 :                 values[i++] = ObjectIdGetDatum(blk->rlocator.relNumber);
     338            0 :                 values[i++] = Int16GetDatum(forknum);
     339            0 :                 values[i++] = Int64GetDatum((int64) blkno);
     340              : 
     341              :                 /* xid, resource_manager, and record_type outputs */
     342            0 :                 values[i++] = TransactionIdGetDatum(XLogRecGetXid(record));
     343            0 :                 values[i++] = CStringGetTextDatum(desc.rm_name);
     344            0 :                 values[i++] = CStringGetTextDatum(record_type);
     345              : 
     346              :                 /*
     347              :                  * record_length, main_data_length, block_data_len, and
     348              :                  * block_fpi_length outputs
     349              :                  */
     350            0 :                 values[i++] = UInt32GetDatum(XLogRecGetTotalLen(record));
     351            0 :                 values[i++] = UInt32GetDatum(XLogRecGetDataLen(record));
     352            0 :                 values[i++] = UInt32GetDatum(block_data_len);
     353            0 :                 values[i++] = UInt32GetDatum(block_fpi_len);
     354              : 
     355              :                 /* block_fpi_info (text array) output */
     356            0 :                 if (block_fpi_info)
     357            0 :                         values[i++] = PointerGetDatum(block_fpi_info);
     358              :                 else
     359            0 :                         nulls[i++] = true;
     360              : 
     361              :                 /* description output (describes WAL record) */
     362            0 :                 if (rec_desc.len > 0)
     363            0 :                         values[i++] = CStringGetTextDatum(rec_desc.data);
     364              :                 else
     365            0 :                         nulls[i++] = true;
     366              : 
     367              :                 /* block_data output */
     368            0 :                 if (blk->has_data && show_data)
     369              :                 {
     370            0 :                         bytea      *block_data;
     371              : 
     372            0 :                         block_data = (bytea *) palloc(block_data_len + VARHDRSZ);
     373            0 :                         SET_VARSIZE(block_data, block_data_len + VARHDRSZ);
     374            0 :                         memcpy(VARDATA(block_data), blk->data, block_data_len);
     375            0 :                         values[i++] = PointerGetDatum(block_data);
     376            0 :                 }
     377              :                 else
     378            0 :                         nulls[i++] = true;
     379              : 
     380              :                 /* block_fpi_data output */
     381            0 :                 if (blk->has_image && show_data)
     382              :                 {
     383            0 :                         PGAlignedBlock buf;
     384            0 :                         Page            page;
     385            0 :                         bytea      *block_fpi_data;
     386              : 
     387            0 :                         page = (Page) buf.data;
     388            0 :                         if (!RestoreBlockImage(record, block_id, page))
     389            0 :                                 ereport(ERROR,
     390              :                                                 (errcode(ERRCODE_INTERNAL_ERROR),
     391              :                                                  errmsg_internal("%s", record->errormsg_buf)));
     392              : 
     393            0 :                         block_fpi_data = (bytea *) palloc(BLCKSZ + VARHDRSZ);
     394            0 :                         SET_VARSIZE(block_fpi_data, BLCKSZ + VARHDRSZ);
     395            0 :                         memcpy(VARDATA(block_fpi_data), page, BLCKSZ);
     396            0 :                         values[i++] = PointerGetDatum(block_fpi_data);
     397            0 :                 }
     398              :                 else
     399            0 :                         nulls[i++] = true;
     400              : 
     401            0 :                 Assert(i == PG_GET_WAL_BLOCK_INFO_COLS);
     402              : 
     403              :                 /* Store a tuple for this block reference */
     404            0 :                 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
     405            0 :                                                          values, nulls);
     406            0 :         }
     407              : 
     408              : #undef PG_GET_WAL_BLOCK_INFO_COLS
     409            0 : }
     410              : 
     411              : /*
     412              :  * Get WAL record info, unnested by block reference
     413              :  */
     414              : Datum
     415            0 : pg_get_wal_block_info(PG_FUNCTION_ARGS)
     416              : {
     417            0 :         XLogRecPtr      start_lsn = PG_GETARG_LSN(0);
     418            0 :         XLogRecPtr      end_lsn = PG_GETARG_LSN(1);
     419            0 :         bool            show_data = PG_GETARG_BOOL(2);
     420            0 :         XLogReaderState *xlogreader;
     421            0 :         MemoryContext old_cxt;
     422            0 :         MemoryContext tmp_cxt;
     423              : 
     424            0 :         ValidateInputLSNs(start_lsn, &end_lsn);
     425              : 
     426            0 :         InitMaterializedSRF(fcinfo, 0);
     427              : 
     428            0 :         xlogreader = InitXLogReaderState(start_lsn);
     429              : 
     430            0 :         tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
     431              :                                                                         "pg_get_wal_block_info temporary cxt",
     432              :                                                                         ALLOCSET_DEFAULT_SIZES);
     433              : 
     434            0 :         while (ReadNextXLogRecord(xlogreader) &&
     435            0 :                    xlogreader->EndRecPtr <= end_lsn)
     436              :         {
     437            0 :                 CHECK_FOR_INTERRUPTS();
     438              : 
     439            0 :                 if (!XLogRecHasAnyBlockRefs(xlogreader))
     440            0 :                         continue;
     441              : 
     442              :                 /* Use the tmp context so we can clean up after each tuple is done */
     443            0 :                 old_cxt = MemoryContextSwitchTo(tmp_cxt);
     444              : 
     445            0 :                 GetWALBlockInfo(fcinfo, xlogreader, show_data);
     446              : 
     447              :                 /* clean up and switch back */
     448            0 :                 MemoryContextSwitchTo(old_cxt);
     449            0 :                 MemoryContextReset(tmp_cxt);
     450              :         }
     451              : 
     452            0 :         MemoryContextDelete(tmp_cxt);
     453            0 :         pfree(xlogreader->private_data);
     454            0 :         XLogReaderFree(xlogreader);
     455              : 
     456            0 :         PG_RETURN_VOID();
     457            0 : }
     458              : 
     459              : /*
     460              :  * Get WAL record info.
     461              :  */
     462              : Datum
     463            0 : pg_get_wal_record_info(PG_FUNCTION_ARGS)
     464              : {
     465              : #define PG_GET_WAL_RECORD_INFO_COLS 11
     466            0 :         Datum           result;
     467            0 :         Datum           values[PG_GET_WAL_RECORD_INFO_COLS] = {0};
     468            0 :         bool            nulls[PG_GET_WAL_RECORD_INFO_COLS] = {0};
     469            0 :         XLogRecPtr      lsn;
     470            0 :         XLogRecPtr      curr_lsn;
     471            0 :         XLogReaderState *xlogreader;
     472            0 :         TupleDesc       tupdesc;
     473            0 :         HeapTuple       tuple;
     474              : 
     475            0 :         lsn = PG_GETARG_LSN(0);
     476            0 :         curr_lsn = GetCurrentLSN();
     477              : 
     478            0 :         if (lsn > curr_lsn)
     479            0 :                 ereport(ERROR,
     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/%08X.",
     483              :                                                    LSN_FORMAT_ARGS(curr_lsn))));
     484              : 
     485              :         /* Build a tuple descriptor for our result type. */
     486            0 :         if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     487            0 :                 elog(ERROR, "return type must be a row type");
     488              : 
     489            0 :         xlogreader = InitXLogReaderState(lsn);
     490              : 
     491            0 :         if (!ReadNextXLogRecord(xlogreader))
     492            0 :                 ereport(ERROR,
     493              :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     494              :                                  errmsg("could not read WAL at %X/%08X",
     495              :                                                 LSN_FORMAT_ARGS(xlogreader->EndRecPtr))));
     496              : 
     497            0 :         GetWALRecordInfo(xlogreader, values, nulls, PG_GET_WAL_RECORD_INFO_COLS);
     498              : 
     499            0 :         pfree(xlogreader->private_data);
     500            0 :         XLogReaderFree(xlogreader);
     501              : 
     502            0 :         tuple = heap_form_tuple(tupdesc, values, nulls);
     503            0 :         result = HeapTupleGetDatum(tuple);
     504              : 
     505            0 :         PG_RETURN_DATUM(result);
     506              : #undef PG_GET_WAL_RECORD_INFO_COLS
     507            0 : }
     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              :  */
     515              : static void
     516            0 : ValidateInputLSNs(XLogRecPtr start_lsn, XLogRecPtr *end_lsn)
     517              : {
     518            0 :         XLogRecPtr      curr_lsn = GetCurrentLSN();
     519              : 
     520            0 :         if (start_lsn > curr_lsn)
     521            0 :                 ereport(ERROR,
     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/%08X.",
     525              :                                                    LSN_FORMAT_ARGS(curr_lsn))));
     526              : 
     527            0 :         if (start_lsn > *end_lsn)
     528            0 :                 ereport(ERROR,
     529              :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     530              :                                  errmsg("WAL start LSN must be less than end LSN")));
     531              : 
     532            0 :         if (*end_lsn > curr_lsn)
     533            0 :                 *end_lsn = curr_lsn;
     534            0 : }
     535              : 
     536              : /*
     537              :  * Get info of all WAL records between start LSN and end LSN.
     538              :  */
     539              : static void
     540            0 : GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
     541              :                                   XLogRecPtr end_lsn)
     542              : {
     543              : #define PG_GET_WAL_RECORDS_INFO_COLS 11
     544            0 :         XLogReaderState *xlogreader;
     545            0 :         ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     546            0 :         MemoryContext old_cxt;
     547            0 :         MemoryContext tmp_cxt;
     548              : 
     549            0 :         Assert(start_lsn <= end_lsn);
     550              : 
     551            0 :         InitMaterializedSRF(fcinfo, 0);
     552              : 
     553            0 :         xlogreader = InitXLogReaderState(start_lsn);
     554              : 
     555            0 :         tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
     556              :                                                                         "GetWALRecordsInfo temporary cxt",
     557              :                                                                         ALLOCSET_DEFAULT_SIZES);
     558              : 
     559            0 :         while (ReadNextXLogRecord(xlogreader) &&
     560            0 :                    xlogreader->EndRecPtr <= end_lsn)
     561              :         {
     562            0 :                 Datum           values[PG_GET_WAL_RECORDS_INFO_COLS] = {0};
     563            0 :                 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            0 :                 old_cxt = MemoryContextSwitchTo(tmp_cxt);
     567              : 
     568            0 :                 GetWALRecordInfo(xlogreader, values, nulls,
     569              :                                                  PG_GET_WAL_RECORDS_INFO_COLS);
     570              : 
     571            0 :                 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
     572            0 :                                                          values, nulls);
     573              : 
     574              :                 /* clean up and switch back */
     575            0 :                 MemoryContextSwitchTo(old_cxt);
     576            0 :                 MemoryContextReset(tmp_cxt);
     577              : 
     578            0 :                 CHECK_FOR_INTERRUPTS();
     579            0 :         }
     580              : 
     581            0 :         MemoryContextDelete(tmp_cxt);
     582            0 :         pfree(xlogreader->private_data);
     583            0 :         XLogReaderFree(xlogreader);
     584              : 
     585              : #undef PG_GET_WAL_RECORDS_INFO_COLS
     586            0 : }
     587              : 
     588              : /*
     589              :  * Get info of all WAL records between start LSN and end LSN.
     590              :  */
     591              : Datum
     592            0 : pg_get_wal_records_info(PG_FUNCTION_ARGS)
     593              : {
     594            0 :         XLogRecPtr      start_lsn = PG_GETARG_LSN(0);
     595            0 :         XLogRecPtr      end_lsn = PG_GETARG_LSN(1);
     596              : 
     597            0 :         ValidateInputLSNs(start_lsn, &end_lsn);
     598            0 :         GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
     599              : 
     600            0 :         PG_RETURN_VOID();
     601            0 : }
     602              : 
     603              : /*
     604              :  * Fill single row of record counts and sizes for an rmgr or record.
     605              :  */
     606              : static void
     607            0 : FillXLogStatsRow(const char *name,
     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            0 :         double          n_pct,
     615              :                                 rec_len_pct,
     616              :                                 fpi_len_pct,
     617              :                                 tot_len_pct;
     618            0 :         int                     i = 0;
     619              : 
     620            0 :         n_pct = 0;
     621            0 :         if (total_count != 0)
     622            0 :                 n_pct = 100 * (double) n / total_count;
     623              : 
     624            0 :         rec_len_pct = 0;
     625            0 :         if (total_rec_len != 0)
     626            0 :                 rec_len_pct = 100 * (double) rec_len / total_rec_len;
     627              : 
     628            0 :         fpi_len_pct = 0;
     629            0 :         if (total_fpi_len != 0)
     630            0 :                 fpi_len_pct = 100 * (double) fpi_len / total_fpi_len;
     631              : 
     632            0 :         tot_len_pct = 0;
     633            0 :         if (total_len != 0)
     634            0 :                 tot_len_pct = 100 * (double) tot_len / total_len;
     635              : 
     636            0 :         values[i++] = CStringGetTextDatum(name);
     637            0 :         values[i++] = Int64GetDatum(n);
     638            0 :         values[i++] = Float8GetDatum(n_pct);
     639            0 :         values[i++] = Int64GetDatum(rec_len);
     640            0 :         values[i++] = Float8GetDatum(rec_len_pct);
     641            0 :         values[i++] = Int64GetDatum(fpi_len);
     642            0 :         values[i++] = Float8GetDatum(fpi_len_pct);
     643            0 :         values[i++] = Int64GetDatum(tot_len);
     644            0 :         values[i++] = Float8GetDatum(tot_len_pct);
     645              : 
     646            0 :         Assert(i == ncols);
     647            0 : }
     648              : 
     649              : /*
     650              :  * Get summary statistics about the records seen so far.
     651              :  */
     652              : static void
     653            0 : GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
     654              :                                         Datum *values, bool *nulls, uint32 ncols,
     655              :                                         bool stats_per_record)
     656              : {
     657            0 :         MemoryContext old_cxt;
     658            0 :         MemoryContext tmp_cxt;
     659            0 :         uint64          total_count = 0;
     660            0 :         uint64          total_rec_len = 0;
     661            0 :         uint64          total_fpi_len = 0;
     662            0 :         uint64          total_len = 0;
     663            0 :         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            0 :         for (ri = 0; ri <= RM_MAX_ID; ri++)
     670              :         {
     671            0 :                 if (!RmgrIdIsValid(ri))
     672            0 :                         continue;
     673              : 
     674            0 :                 total_count += stats->rmgr_stats[ri].count;
     675            0 :                 total_rec_len += stats->rmgr_stats[ri].rec_len;
     676            0 :                 total_fpi_len += stats->rmgr_stats[ri].fpi_len;
     677            0 :         }
     678            0 :         total_len = total_rec_len + total_fpi_len;
     679              : 
     680            0 :         tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
     681              :                                                                         "GetXLogSummaryStats temporary cxt",
     682              :                                                                         ALLOCSET_DEFAULT_SIZES);
     683              : 
     684            0 :         for (ri = 0; ri <= RM_MAX_ID; ri++)
     685              :         {
     686            0 :                 uint64          count;
     687            0 :                 uint64          rec_len;
     688            0 :                 uint64          fpi_len;
     689            0 :                 uint64          tot_len;
     690            0 :                 RmgrData        desc;
     691              : 
     692            0 :                 if (!RmgrIdIsValid(ri))
     693            0 :                         continue;
     694              : 
     695            0 :                 if (!RmgrIdExists(ri))
     696            0 :                         continue;
     697              : 
     698            0 :                 desc = GetRmgr(ri);
     699              : 
     700            0 :                 if (stats_per_record)
     701              :                 {
     702            0 :                         int                     rj;
     703              : 
     704            0 :                         for (rj = 0; rj < MAX_XLINFO_TYPES; rj++)
     705              :                         {
     706            0 :                                 const char *id;
     707              : 
     708            0 :                                 count = stats->record_stats[ri][rj].count;
     709            0 :                                 rec_len = stats->record_stats[ri][rj].rec_len;
     710            0 :                                 fpi_len = stats->record_stats[ri][rj].fpi_len;
     711            0 :                                 tot_len = rec_len + fpi_len;
     712              : 
     713              :                                 /* Skip undefined combinations and ones that didn't occur */
     714            0 :                                 if (count == 0)
     715            0 :                                         continue;
     716              : 
     717            0 :                                 old_cxt = MemoryContextSwitchTo(tmp_cxt);
     718              : 
     719              :                                 /* the upper four bits in xl_info are the rmgr's */
     720            0 :                                 id = desc.rm_identify(rj << 4);
     721            0 :                                 if (id == NULL)
     722            0 :                                         id = psprintf("UNKNOWN (%x)", rj << 4);
     723              : 
     724            0 :                                 FillXLogStatsRow(psprintf("%s/%s", desc.rm_name, id), count,
     725            0 :                                                                  total_count, rec_len, total_rec_len, fpi_len,
     726            0 :                                                                  total_fpi_len, tot_len, total_len,
     727            0 :                                                                  values, nulls, ncols);
     728              : 
     729            0 :                                 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
     730            0 :                                                                          values, nulls);
     731              : 
     732              :                                 /* clean up and switch back */
     733            0 :                                 MemoryContextSwitchTo(old_cxt);
     734            0 :                                 MemoryContextReset(tmp_cxt);
     735            0 :                         }
     736            0 :                 }
     737              :                 else
     738              :                 {
     739            0 :                         count = stats->rmgr_stats[ri].count;
     740            0 :                         rec_len = stats->rmgr_stats[ri].rec_len;
     741            0 :                         fpi_len = stats->rmgr_stats[ri].fpi_len;
     742            0 :                         tot_len = rec_len + fpi_len;
     743              : 
     744            0 :                         old_cxt = MemoryContextSwitchTo(tmp_cxt);
     745              : 
     746            0 :                         FillXLogStatsRow(desc.rm_name, count, total_count, rec_len,
     747            0 :                                                          total_rec_len, fpi_len, total_fpi_len, tot_len,
     748            0 :                                                          total_len, values, nulls, ncols);
     749              : 
     750            0 :                         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
     751            0 :                                                                  values, nulls);
     752              : 
     753              :                         /* clean up and switch back */
     754            0 :                         MemoryContextSwitchTo(old_cxt);
     755            0 :                         MemoryContextReset(tmp_cxt);
     756              :                 }
     757            0 :         }
     758              : 
     759            0 :         MemoryContextDelete(tmp_cxt);
     760            0 : }
     761              : 
     762              : /*
     763              :  * Get WAL stats between start LSN and end LSN.
     764              :  */
     765              : static void
     766            0 : GetWalStats(FunctionCallInfo fcinfo, XLogRecPtr start_lsn, XLogRecPtr end_lsn,
     767              :                         bool stats_per_record)
     768              : {
     769              : #define PG_GET_WAL_STATS_COLS 9
     770            0 :         XLogReaderState *xlogreader;
     771            0 :         XLogStats       stats = {0};
     772            0 :         ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     773            0 :         Datum           values[PG_GET_WAL_STATS_COLS] = {0};
     774            0 :         bool            nulls[PG_GET_WAL_STATS_COLS] = {0};
     775              : 
     776            0 :         Assert(start_lsn <= end_lsn);
     777              : 
     778            0 :         InitMaterializedSRF(fcinfo, 0);
     779              : 
     780            0 :         xlogreader = InitXLogReaderState(start_lsn);
     781              : 
     782            0 :         while (ReadNextXLogRecord(xlogreader) &&
     783            0 :                    xlogreader->EndRecPtr <= end_lsn)
     784              :         {
     785            0 :                 XLogRecStoreStats(&stats, xlogreader);
     786              : 
     787            0 :                 CHECK_FOR_INTERRUPTS();
     788              :         }
     789              : 
     790            0 :         pfree(xlogreader->private_data);
     791            0 :         XLogReaderFree(xlogreader);
     792              : 
     793            0 :         GetXLogSummaryStats(&stats, rsinfo, values, nulls,
     794              :                                                 PG_GET_WAL_STATS_COLS,
     795            0 :                                                 stats_per_record);
     796              : 
     797              : #undef PG_GET_WAL_STATS_COLS
     798            0 : }
     799              : 
     800              : /*
     801              :  * Get stats of all WAL records between start LSN and end LSN.
     802              :  */
     803              : Datum
     804            0 : pg_get_wal_stats(PG_FUNCTION_ARGS)
     805              : {
     806            0 :         XLogRecPtr      start_lsn = PG_GETARG_LSN(0);
     807            0 :         XLogRecPtr      end_lsn = PG_GETARG_LSN(1);
     808            0 :         bool            stats_per_record = PG_GETARG_BOOL(2);
     809              : 
     810            0 :         ValidateInputLSNs(start_lsn, &end_lsn);
     811            0 :         GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
     812              : 
     813            0 :         PG_RETURN_VOID();
     814            0 : }
     815              : 
     816              : /*
     817              :  * The following functions have been removed in newer versions in 1.1, but
     818              :  * they are kept around for compatibility.
     819              :  */
     820              : Datum
     821            0 : pg_get_wal_records_info_till_end_of_wal(PG_FUNCTION_ARGS)
     822              : {
     823            0 :         XLogRecPtr      start_lsn = PG_GETARG_LSN(0);
     824            0 :         XLogRecPtr      end_lsn = GetCurrentLSN();
     825              : 
     826            0 :         if (start_lsn > end_lsn)
     827            0 :                 ereport(ERROR,
     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/%08X.",
     831              :                                                    LSN_FORMAT_ARGS(end_lsn))));
     832              : 
     833            0 :         GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
     834              : 
     835            0 :         PG_RETURN_VOID();
     836            0 : }
     837              : 
     838              : Datum
     839            0 : pg_get_wal_stats_till_end_of_wal(PG_FUNCTION_ARGS)
     840              : {
     841            0 :         XLogRecPtr      start_lsn = PG_GETARG_LSN(0);
     842            0 :         XLogRecPtr      end_lsn = GetCurrentLSN();
     843            0 :         bool            stats_per_record = PG_GETARG_BOOL(1);
     844              : 
     845            0 :         if (start_lsn > end_lsn)
     846            0 :                 ereport(ERROR,
     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/%08X.",
     850              :                                                    LSN_FORMAT_ARGS(end_lsn))));
     851              : 
     852            0 :         GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
     853              : 
     854            0 :         PG_RETURN_VOID();
     855            0 : }
        

Generated by: LCOV version 2.3.2-1