Commit d401c576 authored by Michael Paquier's avatar Michael Paquier

Extend PageIsVerified() to handle more custom options

This is useful for checks of relation pages without having to load the
pages into the shared buffers, and two cases can make use of that: page
verification in base backups and the online, lock-safe, flavor.

Compatibility is kept with past versions using a macro that calls the
new extended routine with the set of options compatible with the
original version.

Extracted from a larger patch by the same author.

Author: Anastasia Lubennikova
Reviewed-by: Michael Paquier, Julien Rouhaud
Discussion: https://postgr.es/m/608f3476-0598-2514-2c03-e05c7d2b0cbd@postgrespro.ru
parent ba9f18ab
...@@ -443,7 +443,8 @@ RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ...@@ -443,7 +443,8 @@ RelationCopyStorage(SMgrRelation src, SMgrRelation dst,
smgrread(src, forkNum, blkno, buf.data); smgrread(src, forkNum, blkno, buf.data);
if (!PageIsVerified(page, blkno)) if (!PageIsVerifiedExtended(page, blkno,
PIV_LOG_WARNING | PIV_REPORT_STAT))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED), (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("invalid page in block %u of relation %s", errmsg("invalid page in block %u of relation %s",
......
...@@ -625,7 +625,8 @@ ReadBuffer(Relation reln, BlockNumber blockNum) ...@@ -625,7 +625,8 @@ ReadBuffer(Relation reln, BlockNumber blockNum)
* *
* In RBM_NORMAL mode, the page is read from disk, and the page header is * In RBM_NORMAL mode, the page is read from disk, and the page header is
* validated. An error is thrown if the page header is not valid. (But * validated. An error is thrown if the page header is not valid. (But
* note that an all-zero page is considered "valid"; see PageIsVerified().) * note that an all-zero page is considered "valid"; see
* PageIsVerifiedExtended().)
* *
* RBM_ZERO_ON_ERROR is like the normal mode, but if the page header is not * RBM_ZERO_ON_ERROR is like the normal mode, but if the page header is not
* valid, the page is zeroed instead of throwing an error. This is intended * valid, the page is zeroed instead of throwing an error. This is intended
...@@ -917,7 +918,8 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, ...@@ -917,7 +918,8 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
} }
/* check for garbage data */ /* check for garbage data */
if (!PageIsVerified((Page) bufBlock, blockNum)) if (!PageIsVerifiedExtended((Page) bufBlock, blockNum,
PIV_LOG_WARNING | PIV_REPORT_STAT))
{ {
if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages) if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages)
{ {
......
...@@ -61,7 +61,7 @@ PageInit(Page page, Size pageSize, Size specialSize) ...@@ -61,7 +61,7 @@ PageInit(Page page, Size pageSize, Size specialSize)
/* /*
* PageIsVerified * PageIsVerifiedExtended
* Check that the page header and checksum (if any) appear valid. * Check that the page header and checksum (if any) appear valid.
* *
* This is called when a page has just been read in from disk. The idea is * This is called when a page has just been read in from disk. The idea is
...@@ -77,9 +77,15 @@ PageInit(Page page, Size pageSize, Size specialSize) ...@@ -77,9 +77,15 @@ PageInit(Page page, Size pageSize, Size specialSize)
* allow zeroed pages here, and are careful that the page access macros * allow zeroed pages here, and are careful that the page access macros
* treat such a page as empty and without free space. Eventually, VACUUM * treat such a page as empty and without free space. Eventually, VACUUM
* will clean up such a page and make it usable. * will clean up such a page and make it usable.
*
* If flag PIV_LOG_WARNING is set, a WARNING is logged in the event of
* a checksum failure.
*
* If flag PIV_REPORT_STAT is set, a checksum failure is reported directly
* to pgstat.
*/ */
bool bool
PageIsVerified(Page page, BlockNumber blkno) PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
{ {
PageHeader p = (PageHeader) page; PageHeader p = (PageHeader) page;
size_t *pagebytes; size_t *pagebytes;
...@@ -140,11 +146,13 @@ PageIsVerified(Page page, BlockNumber blkno) ...@@ -140,11 +146,13 @@ PageIsVerified(Page page, BlockNumber blkno)
*/ */
if (checksum_failure) if (checksum_failure)
{ {
if ((flags & PIV_LOG_WARNING) != 0)
ereport(WARNING, ereport(WARNING,
(errcode(ERRCODE_DATA_CORRUPTED), (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("page verification failed, calculated checksum %u but expected %u", errmsg("page verification failed, calculated checksum %u but expected %u",
checksum, p->pd_checksum))); checksum, p->pd_checksum)));
if ((flags & PIV_REPORT_STAT) != 0)
pgstat_report_checksum_failure(); pgstat_report_checksum_failure();
if (header_sane && ignore_checksum_failure) if (header_sane && ignore_checksum_failure)
......
...@@ -404,26 +404,36 @@ do { \ ...@@ -404,26 +404,36 @@ do { \
* extern declarations * extern declarations
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
/* flags for PageAddItemExtended() */
#define PAI_OVERWRITE (1 << 0) #define PAI_OVERWRITE (1 << 0)
#define PAI_IS_HEAP (1 << 1) #define PAI_IS_HEAP (1 << 1)
/* flags for PageIsVerifiedExtended() */
#define PIV_LOG_WARNING (1 << 0)
#define PIV_REPORT_STAT (1 << 1)
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap) \ #define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap) \
PageAddItemExtended(page, item, size, offsetNumber, \ PageAddItemExtended(page, item, size, offsetNumber, \
((overwrite) ? PAI_OVERWRITE : 0) | \ ((overwrite) ? PAI_OVERWRITE : 0) | \
((is_heap) ? PAI_IS_HEAP : 0)) ((is_heap) ? PAI_IS_HEAP : 0))
#define PageIsVerified(page, blkno) \
PageIsVerifiedExtended(page, blkno, \
PIV_LOG_WARNING | PIV_REPORT_STAT)
/* /*
* Check that BLCKSZ is a multiple of sizeof(size_t). In PageIsVerified(), * Check that BLCKSZ is a multiple of sizeof(size_t). In
* it is much faster to check if a page is full of zeroes using the native * PageIsVerifiedExtended(), it is much faster to check if a page is
* word size. Note that this assertion is kept within a header to make * full of zeroes using the native word size. Note that this assertion
* sure that StaticAssertDecl() works across various combinations of * is kept within a header to make sure that StaticAssertDecl() works
* platforms and compilers. * across various combinations of platforms and compilers.
*/ */
StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)), StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)),
"BLCKSZ has to be a multiple of sizeof(size_t)"); "BLCKSZ has to be a multiple of sizeof(size_t)");
extern void PageInit(Page page, Size pageSize, Size specialSize); extern void PageInit(Page page, Size pageSize, Size specialSize);
extern bool PageIsVerified(Page page, BlockNumber blkno); extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags);
extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size, extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
OffsetNumber offsetNumber, int flags); OffsetNumber offsetNumber, int flags);
extern Page PageGetTempPage(Page page); extern Page PageGetTempPage(Page page);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment