Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_walsummary.c
4 : : * Prints the contents of WAL summary files.
5 : : *
6 : : * Copyright (c) 2017-2025, 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
604 rhaas@postgresql.org 52 :CBC 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]);
515 michael@paquier.xyz 69 : 5 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_walsummary"));
604 rhaas@postgresql.org 70 : 5 : handle_help_version_opts(argc, argv, progname, help);
71 : :
72 : : /* process command-line options */
341 tgl@sss.pgh.pa.us 73 : 4 : while ((c = getopt_long(argc, argv, "iq",
604 rhaas@postgresql.org 74 [ + + ]: 4 : long_options, &optindex)) != -1)
75 : : {
76 [ + - + ]: 2 : switch (c)
77 : : {
78 : 1 : case 'i':
79 : 1 : opt.individual = true;
80 : 1 : break;
604 rhaas@postgresql.org 81 :UBC 0 : case 'q':
82 : 0 : opt.quiet = true;
83 : 0 : break;
604 rhaas@postgresql.org 84 :CBC 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 : : {
443 peter@eisentraut.org 93 : 1 : pg_log_error("no input files specified");
604 rhaas@postgresql.org 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)
604 rhaas@postgresql.org 108 :UBC 0 : pg_fatal("could not open file \"%s\": %m", ws.filename);
109 : :
604 rhaas@postgresql.org 110 :CBC 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)
604 rhaas@postgresql.org 139 :UBC 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. */
604 rhaas@postgresql.org 144 [ + - ]:CBC 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. */
604 rhaas@postgresql.org 158 :UBC 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. */
604 rhaas@postgresql.org 174 [ - + ]:CBC 1 : if (opt->quiet)
604 rhaas@postgresql.org 175 :UBC 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 : : */
604 rhaas@postgresql.org 183 :CBC 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 : : {
604 rhaas@postgresql.org 195 [ # # # # ]:UBC 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. */
604 rhaas@postgresql.org 203 [ + - ]:CBC 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
604 rhaas@postgresql.org 208 :UBC 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
604 rhaas@postgresql.org 218 :CBC 1 : compare_block_numbers(const void *a, const void *b)
219 : : {
220 : 1 : BlockNumber aa = *(BlockNumber *) a;
221 : 1 : BlockNumber bb = *(BlockNumber *) b;
222 : :
568 nathan@postgresql.or 223 : 1 : return pg_cmp_u32(aa, bb);
224 : : }
225 : :
226 : : /*
227 : : * Error callback.
228 : : */
229 : : void
604 rhaas@postgresql.org 230 :UBC 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
604 rhaas@postgresql.org 245 :CBC 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)
604 rhaas@postgresql.org 251 :UBC 0 : pg_fatal("could not read file \"%s\": %m", ws->filename);
252 : :
604 rhaas@postgresql.org 253 :CBC 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"));
500 peter@eisentraut.org 272 : 1 : printf(_(" -V, --version output version information, then exit\n"));
604 rhaas@postgresql.org 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 : }
|