LCOV - code coverage report
Current view: top level - src/backend/backup - walsummary.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 141 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 10 0
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 0.0 % 114 0

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * walsummary.c
       4                 :             :  *        Functions for accessing and managing WAL summary data.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 2010-2026, PostgreSQL Global Development Group
       7                 :             :  *
       8                 :             :  * src/backend/backup/walsummary.c
       9                 :             :  *
      10                 :             :  *-------------------------------------------------------------------------
      11                 :             :  */
      12                 :             : 
      13                 :             : #include "postgres.h"
      14                 :             : 
      15                 :             : #include <sys/stat.h>
      16                 :             : #include <unistd.h>
      17                 :             : 
      18                 :             : #include "access/xlog_internal.h"
      19                 :             : #include "backup/walsummary.h"
      20                 :             : #include "common/int.h"
      21                 :             : #include "utils/wait_event.h"
      22                 :             : 
      23                 :             : static bool IsWalSummaryFilename(char *filename);
      24                 :             : static int      ListComparatorForWalSummaryFiles(const ListCell *a,
      25                 :             :                                                                                          const ListCell *b);
      26                 :             : 
      27                 :             : /*
      28                 :             :  * Get a list of WAL summaries.
      29                 :             :  *
      30                 :             :  * If tli != 0, only WAL summaries with the indicated TLI will be included.
      31                 :             :  *
      32                 :             :  * If start_lsn != InvalidXLogRecPtr, only summaries that end after the
      33                 :             :  * indicated LSN will be included.
      34                 :             :  *
      35                 :             :  * If end_lsn != InvalidXLogRecPtr, only summaries that start before the
      36                 :             :  * indicated LSN will be included.
      37                 :             :  *
      38                 :             :  * The intent is that you can call GetWalSummaries(tli, start_lsn, end_lsn)
      39                 :             :  * to get all WAL summaries on the indicated timeline that overlap the
      40                 :             :  * specified LSN range.
      41                 :             :  */
      42                 :             : List *
      43                 :           0 : GetWalSummaries(TimeLineID tli, XLogRecPtr start_lsn, XLogRecPtr end_lsn)
      44                 :             : {
      45                 :           0 :         DIR                *sdir;
      46                 :           0 :         struct dirent *dent;
      47                 :           0 :         List       *result = NIL;
      48                 :             : 
      49                 :           0 :         sdir = AllocateDir(XLOGDIR "/summaries");
      50         [ #  # ]:           0 :         while ((dent = ReadDir(sdir, XLOGDIR "/summaries")) != NULL)
      51                 :             :         {
      52                 :           0 :                 WalSummaryFile *ws;
      53                 :           0 :                 uint32          tmp[5];
      54                 :           0 :                 TimeLineID      file_tli;
      55                 :           0 :                 XLogRecPtr      file_start_lsn;
      56                 :           0 :                 XLogRecPtr      file_end_lsn;
      57                 :             : 
      58                 :             :                 /* Decode filename, or skip if it's not in the expected format. */
      59         [ #  # ]:           0 :                 if (!IsWalSummaryFilename(dent->d_name))
      60                 :           0 :                         continue;
      61                 :           0 :                 sscanf(dent->d_name, "%08X%08X%08X%08X%08X",
      62                 :           0 :                            &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4]);
      63                 :           0 :                 file_tli = tmp[0];
      64                 :           0 :                 file_start_lsn = ((uint64) tmp[1]) << 32 | tmp[2];
      65                 :           0 :                 file_end_lsn = ((uint64) tmp[3]) << 32 | tmp[4];
      66                 :             : 
      67                 :             :                 /* Skip if it doesn't match the filter criteria. */
      68   [ #  #  #  # ]:           0 :                 if (tli != 0 && tli != file_tli)
      69                 :           0 :                         continue;
      70   [ #  #  #  # ]:           0 :                 if (XLogRecPtrIsValid(start_lsn) && start_lsn >= file_end_lsn)
      71                 :           0 :                         continue;
      72   [ #  #  #  # ]:           0 :                 if (XLogRecPtrIsValid(end_lsn) && end_lsn <= file_start_lsn)
      73                 :           0 :                         continue;
      74                 :             : 
      75                 :             :                 /* Add it to the list. */
      76                 :           0 :                 ws = palloc_object(WalSummaryFile);
      77                 :           0 :                 ws->tli = file_tli;
      78                 :           0 :                 ws->start_lsn = file_start_lsn;
      79                 :           0 :                 ws->end_lsn = file_end_lsn;
      80                 :           0 :                 result = lappend(result, ws);
      81      [ #  #  # ]:           0 :         }
      82                 :           0 :         FreeDir(sdir);
      83                 :             : 
      84                 :           0 :         return result;
      85                 :           0 : }
      86                 :             : 
      87                 :             : /*
      88                 :             :  * Build a new list of WAL summaries based on an existing list, but filtering
      89                 :             :  * out summaries that don't match the search parameters.
      90                 :             :  *
      91                 :             :  * If tli != 0, only WAL summaries with the indicated TLI will be included.
      92                 :             :  *
      93                 :             :  * If start_lsn != InvalidXLogRecPtr, only summaries that end after the
      94                 :             :  * indicated LSN will be included.
      95                 :             :  *
      96                 :             :  * If end_lsn != InvalidXLogRecPtr, only summaries that start before the
      97                 :             :  * indicated LSN will be included.
      98                 :             :  */
      99                 :             : List *
     100                 :           0 : FilterWalSummaries(List *wslist, TimeLineID tli,
     101                 :             :                                    XLogRecPtr start_lsn, XLogRecPtr end_lsn)
     102                 :             : {
     103                 :           0 :         List       *result = NIL;
     104                 :           0 :         ListCell   *lc;
     105                 :             : 
     106                 :             :         /* Loop over input. */
     107   [ #  #  #  #  :           0 :         foreach(lc, wslist)
                   #  # ]
     108                 :             :         {
     109                 :           0 :                 WalSummaryFile *ws = lfirst(lc);
     110                 :             : 
     111                 :             :                 /* Skip if it doesn't match the filter criteria. */
     112   [ #  #  #  # ]:           0 :                 if (tli != 0 && tli != ws->tli)
     113                 :           0 :                         continue;
     114   [ #  #  #  # ]:           0 :                 if (XLogRecPtrIsValid(start_lsn) && start_lsn > ws->end_lsn)
     115                 :           0 :                         continue;
     116   [ #  #  #  # ]:           0 :                 if (XLogRecPtrIsValid(end_lsn) && end_lsn < ws->start_lsn)
     117                 :           0 :                         continue;
     118                 :             : 
     119                 :             :                 /* Add it to the result list. */
     120                 :           0 :                 result = lappend(result, ws);
     121      [ #  #  # ]:           0 :         }
     122                 :             : 
     123                 :           0 :         return result;
     124                 :           0 : }
     125                 :             : 
     126                 :             : /*
     127                 :             :  * Check whether the supplied list of WalSummaryFile objects covers the
     128                 :             :  * whole range of LSNs from start_lsn to end_lsn. This function ignores
     129                 :             :  * timelines, so the caller should probably filter using the appropriate
     130                 :             :  * timeline before calling this.
     131                 :             :  *
     132                 :             :  * If the whole range of LSNs is covered, returns true, otherwise false.
     133                 :             :  * If false is returned, *missing_lsn is set either to InvalidXLogRecPtr
     134                 :             :  * if there are no WAL summary files in the input list, or to the first LSN
     135                 :             :  * in the range that is not covered by a WAL summary file in the input list.
     136                 :             :  */
     137                 :             : bool
     138                 :           0 : WalSummariesAreComplete(List *wslist, XLogRecPtr start_lsn,
     139                 :             :                                                 XLogRecPtr end_lsn, XLogRecPtr *missing_lsn)
     140                 :             : {
     141                 :           0 :         XLogRecPtr      current_lsn = start_lsn;
     142                 :           0 :         ListCell   *lc;
     143                 :             : 
     144                 :             :         /* Special case for empty list. */
     145         [ #  # ]:           0 :         if (wslist == NIL)
     146                 :             :         {
     147                 :           0 :                 *missing_lsn = InvalidXLogRecPtr;
     148                 :           0 :                 return false;
     149                 :             :         }
     150                 :             : 
     151                 :             :         /* Make a private copy of the list and sort it by start LSN. */
     152                 :           0 :         wslist = list_copy(wslist);
     153                 :           0 :         list_sort(wslist, ListComparatorForWalSummaryFiles);
     154                 :             : 
     155                 :             :         /*
     156                 :             :          * Consider summary files in order of increasing start_lsn, advancing the
     157                 :             :          * known-summarized range from start_lsn toward end_lsn.
     158                 :             :          *
     159                 :             :          * Normally, the summary files should cover non-overlapping WAL ranges,
     160                 :             :          * but this algorithm is intended to be correct even in case of overlap.
     161                 :             :          */
     162   [ #  #  #  #  :           0 :         foreach(lc, wslist)
             #  #  #  # ]
     163                 :             :         {
     164                 :           0 :                 WalSummaryFile *ws = lfirst(lc);
     165                 :             : 
     166         [ #  # ]:           0 :                 if (ws->start_lsn > current_lsn)
     167                 :             :                 {
     168                 :             :                         /* We found a gap. */
     169                 :           0 :                         break;
     170                 :             :                 }
     171         [ #  # ]:           0 :                 if (ws->end_lsn > current_lsn)
     172                 :             :                 {
     173                 :             :                         /*
     174                 :             :                          * Next summary extends beyond end of previous summary, so extend
     175                 :             :                          * the end of the range known to be summarized.
     176                 :             :                          */
     177                 :           0 :                         current_lsn = ws->end_lsn;
     178                 :             : 
     179                 :             :                         /*
     180                 :             :                          * If the range we know to be summarized has reached the required
     181                 :             :                          * end LSN, we have proved completeness.
     182                 :             :                          */
     183         [ #  # ]:           0 :                         if (current_lsn >= end_lsn)
     184                 :           0 :                                 return true;
     185                 :           0 :                 }
     186         [ #  # ]:           0 :         }
     187                 :             : 
     188                 :             :         /*
     189                 :             :          * We either ran out of summary files without reaching the end LSN, or we
     190                 :             :          * hit a gap in the sequence that resulted in us bailing out of the loop
     191                 :             :          * above.
     192                 :             :          */
     193                 :           0 :         *missing_lsn = current_lsn;
     194                 :           0 :         return false;
     195                 :           0 : }
     196                 :             : 
     197                 :             : /*
     198                 :             :  * Open a WAL summary file.
     199                 :             :  *
     200                 :             :  * This will throw an error in case of trouble. As an exception, if
     201                 :             :  * missing_ok = true and the trouble is specifically that the file does
     202                 :             :  * not exist, it will not throw an error and will return a value less than 0.
     203                 :             :  */
     204                 :             : File
     205                 :           0 : OpenWalSummaryFile(WalSummaryFile *ws, bool missing_ok)
     206                 :             : {
     207                 :           0 :         char            path[MAXPGPATH];
     208                 :           0 :         File            file;
     209                 :             : 
     210                 :           0 :         snprintf(path, MAXPGPATH,
     211                 :             :                          XLOGDIR "/summaries/%08X%08X%08X%08X%08X.summary",
     212                 :           0 :                          ws->tli,
     213                 :           0 :                          LSN_FORMAT_ARGS(ws->start_lsn),
     214                 :           0 :                          LSN_FORMAT_ARGS(ws->end_lsn));
     215                 :             : 
     216                 :           0 :         file = PathNameOpenFile(path, O_RDONLY);
     217   [ #  #  #  # ]:           0 :         if (file < 0 && (errno != EEXIST || !missing_ok))
     218   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     219                 :             :                                 (errcode_for_file_access(),
     220                 :             :                                  errmsg("could not open file \"%s\": %m", path)));
     221                 :             : 
     222                 :           0 :         return file;
     223                 :           0 : }
     224                 :             : 
     225                 :             : /*
     226                 :             :  * Remove a WAL summary file if the last modification time precedes the
     227                 :             :  * cutoff time.
     228                 :             :  */
     229                 :             : void
     230                 :           0 : RemoveWalSummaryIfOlderThan(WalSummaryFile *ws, time_t cutoff_time)
     231                 :             : {
     232                 :           0 :         char            path[MAXPGPATH];
     233                 :           0 :         struct stat statbuf;
     234                 :             : 
     235                 :           0 :         snprintf(path, MAXPGPATH,
     236                 :             :                          XLOGDIR "/summaries/%08X%08X%08X%08X%08X.summary",
     237                 :           0 :                          ws->tli,
     238                 :           0 :                          LSN_FORMAT_ARGS(ws->start_lsn),
     239                 :           0 :                          LSN_FORMAT_ARGS(ws->end_lsn));
     240                 :             : 
     241         [ #  # ]:           0 :         if (lstat(path, &statbuf) != 0)
     242                 :             :         {
     243         [ #  # ]:           0 :                 if (errno == ENOENT)
     244                 :           0 :                         return;
     245   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     246                 :             :                                 (errcode_for_file_access(),
     247                 :             :                                  errmsg("could not stat file \"%s\": %m", path)));
     248                 :           0 :         }
     249         [ #  # ]:           0 :         if (statbuf.st_mtime >= cutoff_time)
     250                 :           0 :                 return;
     251         [ #  # ]:           0 :         if (unlink(path) != 0)
     252   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     253                 :             :                                 (errcode_for_file_access(),
     254                 :             :                                  errmsg("could not stat file \"%s\": %m", path)));
     255   [ #  #  #  # ]:           0 :         ereport(DEBUG2,
     256                 :             :                         (errmsg_internal("removing file \"%s\"", path)));
     257         [ #  # ]:           0 : }
     258                 :             : 
     259                 :             : /*
     260                 :             :  * Test whether a filename looks like a WAL summary file.
     261                 :             :  */
     262                 :             : static bool
     263                 :           0 : IsWalSummaryFilename(char *filename)
     264                 :             : {
     265         [ #  # ]:           0 :         return strspn(filename, "0123456789ABCDEF") == 40 &&
     266                 :           0 :                 strcmp(filename + 40, ".summary") == 0;
     267                 :             : }
     268                 :             : 
     269                 :             : /*
     270                 :             :  * Data read callback for use with CreateBlockRefTableReader.
     271                 :             :  */
     272                 :             : int
     273                 :           0 : ReadWalSummary(void *wal_summary_io, void *data, int length)
     274                 :             : {
     275                 :           0 :         WalSummaryIO *io = wal_summary_io;
     276                 :           0 :         int                     nbytes;
     277                 :             : 
     278                 :           0 :         nbytes = FileRead(io->file, data, length, io->filepos,
     279                 :             :                                           WAIT_EVENT_WAL_SUMMARY_READ);
     280         [ #  # ]:           0 :         if (nbytes < 0)
     281   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     282                 :             :                                 (errcode_for_file_access(),
     283                 :             :                                  errmsg("could not read file \"%s\": %m",
     284                 :             :                                                 FilePathName(io->file))));
     285                 :             : 
     286                 :           0 :         io->filepos += nbytes;
     287                 :           0 :         return nbytes;
     288                 :           0 : }
     289                 :             : 
     290                 :             : /*
     291                 :             :  * Data write callback for use with WriteBlockRefTable.
     292                 :             :  */
     293                 :             : int
     294                 :           0 : WriteWalSummary(void *wal_summary_io, void *data, int length)
     295                 :             : {
     296                 :           0 :         WalSummaryIO *io = wal_summary_io;
     297                 :           0 :         int                     nbytes;
     298                 :             : 
     299                 :           0 :         nbytes = FileWrite(io->file, data, length, io->filepos,
     300                 :             :                                            WAIT_EVENT_WAL_SUMMARY_WRITE);
     301         [ #  # ]:           0 :         if (nbytes < 0)
     302   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     303                 :             :                                 (errcode_for_file_access(),
     304                 :             :                                  errmsg("could not write file \"%s\": %m",
     305                 :             :                                                 FilePathName(io->file))));
     306         [ #  # ]:           0 :         if (nbytes != length)
     307   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     308                 :             :                                 (errcode_for_file_access(),
     309                 :             :                                  errmsg("could not write file \"%s\": wrote only %d of %d bytes at offset %u",
     310                 :             :                                                 FilePathName(io->file), nbytes,
     311                 :             :                                                 length, (unsigned) io->filepos),
     312                 :             :                                  errhint("Check free disk space.")));
     313                 :             : 
     314                 :           0 :         io->filepos += nbytes;
     315                 :           0 :         return nbytes;
     316                 :           0 : }
     317                 :             : 
     318                 :             : /*
     319                 :             :  * Error-reporting callback for use with CreateBlockRefTableReader.
     320                 :             :  */
     321                 :             : void
     322                 :           0 : ReportWalSummaryError(void *callback_arg, char *fmt,...)
     323                 :             : {
     324                 :           0 :         StringInfoData buf;
     325                 :           0 :         va_list         ap;
     326                 :           0 :         int                     needed;
     327                 :             : 
     328                 :           0 :         initStringInfo(&buf);
     329                 :           0 :         for (;;)
     330                 :             :         {
     331                 :           0 :                 va_start(ap, fmt);
     332                 :           0 :                 needed = appendStringInfoVA(&buf, fmt, ap);
     333                 :           0 :                 va_end(ap);
     334         [ #  # ]:           0 :                 if (needed == 0)
     335                 :           0 :                         break;
     336                 :           0 :                 enlargeStringInfo(&buf, needed);
     337                 :             :         }
     338   [ #  #  #  # ]:           0 :         ereport(ERROR,
     339                 :             :                         errcode(ERRCODE_DATA_CORRUPTED),
     340                 :             :                         errmsg_internal("%s", buf.data));
     341                 :           0 : }
     342                 :             : 
     343                 :             : /*
     344                 :             :  * Comparator to sort a List of WalSummaryFile objects by start_lsn.
     345                 :             :  */
     346                 :             : static int
     347                 :           0 : ListComparatorForWalSummaryFiles(const ListCell *a, const ListCell *b)
     348                 :             : {
     349                 :           0 :         WalSummaryFile *ws1 = lfirst(a);
     350                 :           0 :         WalSummaryFile *ws2 = lfirst(b);
     351                 :             : 
     352                 :           0 :         return pg_cmp_u64(ws1->start_lsn, ws2->start_lsn);
     353                 :           0 : }
        

Generated by: LCOV version 2.3.2-1