LCOV - code coverage report
Current view: top level - src/bin/pg_walsummary - pg_walsummary.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 71.9 % 89 64
Test Date: 2026-07-03 19:57:34 Functions: 83.3 % 6 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 57.1 % 35 20

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * pg_walsummary.c
       4                 :             :  *      Prints the contents of WAL summary files.
       5                 :             :  *
       6                 :             :  * Copyright (c) 2017-2026, PostgreSQL Global Development Group
       7                 :             :  *
       8                 :             :  * IDENTIFICATION
       9                 :             :  *    src/bin/pg_walsummary/pg_walsummary.c
      10                 :             :  *
      11                 :             :  *-------------------------------------------------------------------------
      12                 :             :  */
      13                 :             : #include "postgres_fe.h"
      14                 :             : 
      15                 :             : #include <fcntl.h>
      16                 :             : #include <limits.h>
      17                 :             : 
      18                 :             : #include "common/blkreftable.h"
      19                 :             : #include "common/int.h"
      20                 :             : #include "common/logging.h"
      21                 :             : #include "fe_utils/option_utils.h"
      22                 :             : #include "getopt_long.h"
      23                 :             : 
      24                 :             : typedef struct ws_options
      25                 :             : {
      26                 :             :     bool        individual;
      27                 :             :     bool        quiet;
      28                 :             : } ws_options;
      29                 :             : 
      30                 :             : typedef struct ws_file_info
      31                 :             : {
      32                 :             :     int         fd;
      33                 :             :     char       *filename;
      34                 :             : } ws_file_info;
      35                 :             : 
      36                 :             : static BlockNumber *block_buffer = NULL;
      37                 :             : static unsigned block_buffer_size = 512;    /* Initial size. */
      38                 :             : 
      39                 :             : static void dump_one_relation(ws_options *opt, RelFileLocator *rlocator,
      40                 :             :                               ForkNumber forknum, BlockNumber limit_block,
      41                 :             :                               BlockRefTableReader *reader);
      42                 :             : static void help(const char *progname);
      43                 :             : static int  compare_block_numbers(const void *a, const void *b);
      44                 :             : static int  walsummary_read_callback(void *callback_arg, void *data,
      45                 :             :                                      int length);
      46                 :             : static void walsummary_error_callback(void *callback_arg, char *fmt, ...) pg_attribute_printf(2, 3);
      47                 :             : 
      48                 :             : /*
      49                 :             :  * Main program.
      50                 :             :  */
      51                 :             : int
      52                 :           5 : main(int argc, char *argv[])
      53                 :             : {
      54                 :             :     static struct option long_options[] = {
      55                 :             :         {"individual", no_argument, NULL, 'i'},
      56                 :             :         {"quiet", no_argument, NULL, 'q'},
      57                 :             :         {NULL, 0, NULL, 0}
      58                 :             :     };
      59                 :             : 
      60                 :             :     const char *progname;
      61                 :             :     int         optindex;
      62                 :             :     int         c;
      63                 :             :     ws_options  opt;
      64                 :             : 
      65                 :           5 :     memset(&opt, 0, sizeof(ws_options));
      66                 :             : 
      67                 :           5 :     pg_logging_init(argv[0]);
      68                 :           5 :     progname = get_progname(argv[0]);
      69                 :           5 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_walsummary"));
      70                 :           5 :     handle_help_version_opts(argc, argv, progname, help);
      71                 :             : 
      72                 :             :     /* process command-line options */
      73                 :           4 :     while ((c = getopt_long(argc, argv, "iq",
      74         [ +  + ]:           4 :                             long_options, &optindex)) != -1)
      75                 :             :     {
      76      [ +  -  + ]:           2 :         switch (c)
      77                 :             :         {
      78                 :           1 :             case 'i':
      79                 :           1 :                 opt.individual = true;
      80                 :           1 :                 break;
      81                 :           0 :             case 'q':
      82                 :           0 :                 opt.quiet = true;
      83                 :           0 :                 break;
      84                 :           1 :             default:
      85                 :             :                 /* getopt_long already emitted a complaint */
      86                 :           1 :                 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
      87                 :           1 :                 exit(1);
      88                 :             :         }
      89                 :             :     }
      90                 :             : 
      91         [ +  + ]:           2 :     if (optind >= argc)
      92                 :             :     {
      93                 :           1 :         pg_log_error("no input files specified");
      94                 :           1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
      95                 :           1 :         exit(1);
      96                 :             :     }
      97                 :             : 
      98         [ +  + ]:           2 :     while (optind < argc)
      99                 :             :     {
     100                 :             :         ws_file_info ws;
     101                 :             :         BlockRefTableReader *reader;
     102                 :             :         RelFileLocator rlocator;
     103                 :             :         ForkNumber  forknum;
     104                 :             :         BlockNumber limit_block;
     105                 :             : 
     106                 :           1 :         ws.filename = argv[optind++];
     107         [ -  + ]:           1 :         if ((ws.fd = open(ws.filename, O_RDONLY | PG_BINARY, 0)) < 0)
     108                 :           0 :             pg_fatal("could not open file \"%s\": %m", ws.filename);
     109                 :             : 
     110                 :           1 :         reader = CreateBlockRefTableReader(walsummary_read_callback, &ws,
     111                 :             :                                            ws.filename,
     112                 :             :                                            walsummary_error_callback, NULL);
     113         [ +  + ]:           2 :         while (BlockRefTableReaderNextRelation(reader, &rlocator, &forknum,
     114                 :             :                                                &limit_block))
     115                 :           1 :             dump_one_relation(&opt, &rlocator, forknum, limit_block, reader);
     116                 :             : 
     117                 :           1 :         DestroyBlockRefTableReader(reader);
     118                 :           1 :         close(ws.fd);
     119                 :             :     }
     120                 :             : 
     121                 :           1 :     exit(0);
     122                 :             : }
     123                 :             : 
     124                 :             : /*
     125                 :             :  * Dump details for one relation.
     126                 :             :  */
     127                 :             : static void
     128                 :           1 : dump_one_relation(ws_options *opt, RelFileLocator *rlocator,
     129                 :             :                   ForkNumber forknum, BlockNumber limit_block,
     130                 :             :                   BlockRefTableReader *reader)
     131                 :             : {
     132                 :           1 :     unsigned    i = 0;
     133                 :             :     unsigned    nblocks;
     134                 :           1 :     BlockNumber startblock = InvalidBlockNumber;
     135                 :           1 :     BlockNumber endblock = InvalidBlockNumber;
     136                 :             : 
     137                 :             :     /* Dump limit block, if any. */
     138         [ -  + ]:           1 :     if (limit_block != InvalidBlockNumber)
     139                 :           0 :         printf("TS %u, DB %u, REL %u, FORK %s: limit %u\n",
     140                 :             :                rlocator->spcOid, rlocator->dbOid, rlocator->relNumber,
     141                 :             :                forkNames[forknum], limit_block);
     142                 :             : 
     143                 :             :     /* If we haven't allocated a block buffer yet, do that now. */
     144         [ +  - ]:           1 :     if (block_buffer == NULL)
     145                 :           1 :         block_buffer = palloc_array(BlockNumber, block_buffer_size);
     146                 :             : 
     147                 :             :     /* Try to fill the block buffer. */
     148                 :           1 :     nblocks = BlockRefTableReaderGetBlocks(reader,
     149                 :             :                                            block_buffer,
     150                 :             :                                            block_buffer_size);
     151                 :             : 
     152                 :             :     /* If we filled the block buffer completely, we must enlarge it. */
     153         [ -  + ]:           1 :     while (nblocks >= block_buffer_size)
     154                 :             :     {
     155                 :             :         unsigned    new_size;
     156                 :             : 
     157                 :             :         /* Double the size, being careful about overflow. */
     158                 :           0 :         new_size = block_buffer_size * 2;
     159         [ #  # ]:           0 :         if (new_size < block_buffer_size)
     160                 :           0 :             new_size = PG_UINT32_MAX;
     161                 :           0 :         block_buffer = repalloc_array(block_buffer, BlockNumber, new_size);
     162                 :             : 
     163                 :             :         /* Try to fill the newly-allocated space. */
     164                 :           0 :         nblocks +=
     165                 :           0 :             BlockRefTableReaderGetBlocks(reader,
     166                 :           0 :                                          block_buffer + block_buffer_size,
     167                 :           0 :                                          new_size - block_buffer_size);
     168                 :             : 
     169                 :             :         /* Save the new size for later calls. */
     170                 :           0 :         block_buffer_size = new_size;
     171                 :             :     }
     172                 :             : 
     173                 :             :     /* If we don't need to produce any output, skip the rest of this. */
     174         [ -  + ]:           1 :     if (opt->quiet)
     175                 :           0 :         return;
     176                 :             : 
     177                 :             :     /*
     178                 :             :      * Sort the returned block numbers. If the block reference table was using
     179                 :             :      * the bitmap representation for a given chunk, the block numbers in that
     180                 :             :      * chunk will already be sorted, but when the array-of-offsets
     181                 :             :      * representation is used, we can receive block numbers here out of order.
     182                 :             :      */
     183                 :           1 :     qsort(block_buffer, nblocks, sizeof(BlockNumber), compare_block_numbers);
     184                 :             : 
     185                 :             :     /* Dump block references. */
     186         [ +  + ]:           3 :     while (i < nblocks)
     187                 :             :     {
     188                 :             :         /*
     189                 :             :          * Find the next range of blocks to print, but if --individual was
     190                 :             :          * specified, then consider each block a separate range.
     191                 :             :          */
     192                 :           2 :         startblock = endblock = block_buffer[i++];
     193         [ -  + ]:           2 :         if (!opt->individual)
     194                 :             :         {
     195   [ #  #  #  # ]:           0 :             while (i < nblocks && block_buffer[i] == endblock + 1)
     196                 :             :             {
     197                 :           0 :                 endblock++;
     198                 :           0 :                 i++;
     199                 :             :             }
     200                 :             :         }
     201                 :             : 
     202                 :             :         /* Format this range of block numbers as a string. */
     203         [ +  - ]:           2 :         if (startblock == endblock)
     204                 :           2 :             printf("TS %u, DB %u, REL %u, FORK %s: block %u\n",
     205                 :             :                    rlocator->spcOid, rlocator->dbOid, rlocator->relNumber,
     206                 :             :                    forkNames[forknum], startblock);
     207                 :             :         else
     208                 :           0 :             printf("TS %u, DB %u, REL %u, FORK %s: blocks %u..%u\n",
     209                 :             :                    rlocator->spcOid, rlocator->dbOid, rlocator->relNumber,
     210                 :             :                    forkNames[forknum], startblock, endblock);
     211                 :             :     }
     212                 :             : }
     213                 :             : 
     214                 :             : /*
     215                 :             :  * Quicksort comparator for block numbers.
     216                 :             :  */
     217                 :             : static int
     218                 :           1 : compare_block_numbers(const void *a, const void *b)
     219                 :             : {
     220                 :           1 :     BlockNumber aa = *(const BlockNumber *) a;
     221                 :           1 :     BlockNumber bb = *(const BlockNumber *) b;
     222                 :             : 
     223                 :           1 :     return pg_cmp_u32(aa, bb);
     224                 :             : }
     225                 :             : 
     226                 :             : /*
     227                 :             :  * Error callback.
     228                 :             :  */
     229                 :             : void
     230                 :           0 : walsummary_error_callback(void *callback_arg, char *fmt, ...)
     231                 :             : {
     232                 :             :     va_list     ap;
     233                 :             : 
     234                 :           0 :     va_start(ap, fmt);
     235                 :           0 :     pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, ap);
     236                 :           0 :     va_end(ap);
     237                 :             : 
     238                 :           0 :     exit(1);
     239                 :             : }
     240                 :             : 
     241                 :             : /*
     242                 :             :  * Read callback.
     243                 :             :  */
     244                 :             : int
     245                 :           1 : walsummary_read_callback(void *callback_arg, void *data, int length)
     246                 :             : {
     247                 :           1 :     ws_file_info *ws = callback_arg;
     248                 :             :     int         rc;
     249                 :             : 
     250         [ -  + ]:           1 :     if ((rc = read(ws->fd, data, length)) < 0)
     251                 :           0 :         pg_fatal("could not read file \"%s\": %m", ws->filename);
     252                 :             : 
     253                 :           1 :     return rc;
     254                 :             : }
     255                 :             : 
     256                 :             : /*
     257                 :             :  * help
     258                 :             :  *
     259                 :             :  * Prints help page for the program
     260                 :             :  *
     261                 :             :  * progname: the name of the executed program, such as "pg_walsummary"
     262                 :             :  */
     263                 :             : static void
     264                 :           1 : help(const char *progname)
     265                 :             : {
     266                 :           1 :     printf(_("%s prints the contents of a WAL summary file.\n\n"), progname);
     267                 :           1 :     printf(_("Usage:\n"));
     268                 :           1 :     printf(_("  %s [OPTION]... FILE...\n"), progname);
     269                 :           1 :     printf(_("\nOptions:\n"));
     270                 :           1 :     printf(_("  -i, --individual          list block numbers individually, not as ranges\n"));
     271                 :           1 :     printf(_("  -q, --quiet               don't print anything, just parse the files\n"));
     272                 :           1 :     printf(_("  -V, --version             output version information, then exit\n"));
     273                 :           1 :     printf(_("  -?, --help                show this help, then exit\n"));
     274                 :             : 
     275                 :           1 :     printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
     276                 :           1 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
     277                 :           1 : }
        

Generated by: LCOV version 2.0-1