/*------------------------------------------------------------------------- * * pg_walsummary.c * Prints the contents of WAL summary files. * * Copyright (c) 2017-2024, PostgreSQL Global Development Group * * IDENTIFICATION * src/bin/pg_walsummary/pg_walsummary.c * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include #include #include "common/blkreftable.h" #include "common/int.h" #include "common/logging.h" #include "fe_utils/option_utils.h" #include "getopt_long.h" #include "lib/stringinfo.h" typedef struct ws_options { bool individual; bool quiet; } ws_options; typedef struct ws_file_info { int fd; char *filename; } ws_file_info; static BlockNumber *block_buffer = NULL; static unsigned block_buffer_size = 512; /* Initial size. */ static void dump_one_relation(ws_options *opt, RelFileLocator *rlocator, ForkNumber forknum, BlockNumber limit_block, BlockRefTableReader *reader); static void help(const char *progname); static int compare_block_numbers(const void *a, const void *b); static int walsummary_read_callback(void *callback_arg, void *data, int length); static void walsummary_error_callback(void *callback_arg, char *fmt,...) pg_attribute_printf(2, 3); /* * Main program. */ int main(int argc, char *argv[]) { static struct option long_options[] = { {"individual", no_argument, NULL, 'i'}, {"quiet", no_argument, NULL, 'q'}, {NULL, 0, NULL, 0} }; const char *progname; int optindex; int c; ws_options opt; memset(&opt, 0, sizeof(ws_options)); pg_logging_init(argv[0]); progname = get_progname(argv[0]); handle_help_version_opts(argc, argv, progname, help); /* process command-line options */ while ((c = getopt_long(argc, argv, "f:iqw:", long_options, &optindex)) != -1) { switch (c) { case 'i': opt.individual = true; break; case 'q': opt.quiet = true; break; default: /* getopt_long already emitted a complaint */ pg_log_error_hint("Try \"%s --help\" for more information.", progname); exit(1); } } if (optind >= argc) { pg_log_error("%s: no input files specified", progname); pg_log_error_hint("Try \"%s --help\" for more information.", progname); exit(1); } while (optind < argc) { ws_file_info ws; BlockRefTableReader *reader; RelFileLocator rlocator; ForkNumber forknum; BlockNumber limit_block; ws.filename = argv[optind++]; if ((ws.fd = open(ws.filename, O_RDONLY | PG_BINARY, 0)) < 0) pg_fatal("could not open file \"%s\": %m", ws.filename); reader = CreateBlockRefTableReader(walsummary_read_callback, &ws, ws.filename, walsummary_error_callback, NULL); while (BlockRefTableReaderNextRelation(reader, &rlocator, &forknum, &limit_block)) dump_one_relation(&opt, &rlocator, forknum, limit_block, reader); DestroyBlockRefTableReader(reader); close(ws.fd); } exit(0); } /* * Dump details for one relation. */ static void dump_one_relation(ws_options *opt, RelFileLocator *rlocator, ForkNumber forknum, BlockNumber limit_block, BlockRefTableReader *reader) { unsigned i = 0; unsigned nblocks; BlockNumber startblock = InvalidBlockNumber; BlockNumber endblock = InvalidBlockNumber; /* Dump limit block, if any. */ if (limit_block != InvalidBlockNumber) printf("TS %u, DB %u, REL %u, FORK %s: limit %u\n", rlocator->spcOid, rlocator->dbOid, rlocator->relNumber, forkNames[forknum], limit_block); /* If we haven't allocated a block buffer yet, do that now. */ if (block_buffer == NULL) block_buffer = palloc_array(BlockNumber, block_buffer_size); /* Try to fill the block buffer. */ nblocks = BlockRefTableReaderGetBlocks(reader, block_buffer, block_buffer_size); /* If we filled the block buffer completely, we must enlarge it. */ while (nblocks >= block_buffer_size) { unsigned new_size; /* Double the size, being careful about overflow. */ new_size = block_buffer_size * 2; if (new_size < block_buffer_size) new_size = PG_UINT32_MAX; block_buffer = repalloc_array(block_buffer, BlockNumber, new_size); /* Try to fill the newly-allocated space. */ nblocks += BlockRefTableReaderGetBlocks(reader, block_buffer + block_buffer_size, new_size - block_buffer_size); /* Save the new size for later calls. */ block_buffer_size = new_size; } /* If we don't need to produce any output, skip the rest of this. */ if (opt->quiet) return; /* * Sort the returned block numbers. If the block reference table was using * the bitmap representation for a given chunk, the block numbers in that * chunk will already be sorted, but when the array-of-offsets * representation is used, we can receive block numbers here out of order. */ qsort(block_buffer, nblocks, sizeof(BlockNumber), compare_block_numbers); /* Dump block references. */ while (i < nblocks) { /* * Find the next range of blocks to print, but if --individual was * specified, then consider each block a separate range. */ startblock = endblock = block_buffer[i++]; if (!opt->individual) { while (i < nblocks && block_buffer[i] == endblock + 1) { endblock++; i++; } } /* Format this range of block numbers as a string. */ if (startblock == endblock) printf("TS %u, DB %u, REL %u, FORK %s: block %u\n", rlocator->spcOid, rlocator->dbOid, rlocator->relNumber, forkNames[forknum], startblock); else printf("TS %u, DB %u, REL %u, FORK %s: blocks %u..%u\n", rlocator->spcOid, rlocator->dbOid, rlocator->relNumber, forkNames[forknum], startblock, endblock); } } /* * Quicksort comparator for block numbers. */ static int compare_block_numbers(const void *a, const void *b) { BlockNumber aa = *(BlockNumber *) a; BlockNumber bb = *(BlockNumber *) b; return pg_cmp_u32(aa, bb); } /* * Error callback. */ void walsummary_error_callback(void *callback_arg, char *fmt,...) { va_list ap; va_start(ap, fmt); pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, ap); va_end(ap); exit(1); } /* * Read callback. */ int walsummary_read_callback(void *callback_arg, void *data, int length) { ws_file_info *ws = callback_arg; int rc; if ((rc = read(ws->fd, data, length)) < 0) pg_fatal("could not read file \"%s\": %m", ws->filename); return rc; } /* * help * * Prints help page for the program * * progname: the name of the executed program, such as "pg_walsummary" */ static void help(const char *progname) { printf(_("%s prints the contents of a WAL summary file.\n\n"), progname); printf(_("Usage:\n")); printf(_(" %s [OPTION]... FILE...\n"), progname); printf(_("\nOptions:\n")); printf(_(" -i, --individual list block numbers individually, not as ranges\n")); printf(_(" -q, --quiet don't print anything, just parse the files\n")); printf(_(" -?, --help show this help, then exit\n")); printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); }