From a62de1b7b467a037651a2e1bb3820a390227ce78 Mon Sep 17 00:00:00 2001 From: Amul Sul Date: Sat, 21 Mar 2026 20:57:23 +0530 Subject: [PATCH v2 3/3] pg_waldump: Handle archive exhaustion in init_archive_reader(). When read_archive_file() returns 0, the archive may have already buffered a complete WAL file into the hash table before exhausting the input. Instead of immediately reporting an error, search the hash table for an entry containing at least sizeof(XLogLongPageHeader) bytes. Report a specific error if a WAL entry exists but is too short (truncated/corrupt), or a generic error if no WAL was found at all. Also tighten the loop condition to check for sizeof(XLogLongPageHeader) rather than XLOG_BLCKSZ, since only the long page header is needed at this stage. --- src/bin/pg_waldump/archive_waldump.c | 55 ++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/src/bin/pg_waldump/archive_waldump.c b/src/bin/pg_waldump/archive_waldump.c index 1e9ae637940..dbc1751fb3c 100644 --- a/src/bin/pg_waldump/archive_waldump.c +++ b/src/bin/pg_waldump/archive_waldump.c @@ -176,13 +176,60 @@ init_archive_reader(XLogDumpPrivate *privateInfo, * the first WAL segment in the archive so we can extract the WAL segment * size from the long page header. */ - while (entry == NULL || entry->buf->len < XLOG_BLCKSZ) + while (entry == NULL || entry->read_len < sizeof(XLogLongPageHeader)) { if (read_archive_file(privateInfo, XLOG_BLCKSZ) == 0) - pg_fatal("could not find WAL in archive \"%s\"", - privateInfo->archive_name); + { + ArchivedWAL_iterator iter; + ArchivedWALFile *e = NULL; + ArchivedWALFile *short_entry = NULL; - entry = privateInfo->cur_file; + entry = NULL; + + /* + * read_archive_file() returned 0, meaning the archive is + * exhausted. However, a sufficiently compressed archive may have + * already read a complete WAL file and inserted it into the hash + * table before returning. Search the hash table for any entry + * that already has enough buffered data to contain the long page + * header; if none is found, the archive contains no usable WAL. + */ + ArchivedWAL_start_iterate(privateInfo->archive_wal_htab, &iter); + while ((e = ArchivedWAL_iterate(privateInfo->archive_wal_htab, + &iter)) != NULL) + { + if (e->read_len >= sizeof(XLogLongPageHeader)) + { + entry = e; + break; + } + /* Remember a short entry in case we need to report it */ + short_entry = e; + } + + if (entry == NULL) + { + /* + * A WAL file was found in the hash table but it does not + * contain enough data to read the long page header, + * indicating a truncated or corrupt WAL segment. + */ + if (short_entry != NULL) + pg_fatal("could not read file \"%s\" from \"%s\" archive: read %d of %d", + short_entry->fname, privateInfo->archive_name, + short_entry->read_len, + (int) sizeof(XLogLongPageHeader)); + + /* + * The hash table contains no WAL entries at all, meaning the + * archive holds no WAL data. + */ + pg_fatal("could not find WAL in archive \"%s\"", + privateInfo->archive_name); + } + } + else + entry = privateInfo->cur_file; } /* Extract the WAL segment size from the long page header */ -- 2.47.1