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

Generated by: LCOV version 2.0-1