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

Generated by: LCOV version 1.14