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