Add code to apply some simple sanity checks to the header fields of a

page when it's read in, per pghackers discussion around 17-Feb.  Add a
GUC variable zero_damaged_pages that causes the response to be a WARNING
followed by zeroing the page, rather than the normal ERROR; this is per
Hiroshi's suggestion that there needs to be a way to get at the data
in the rest of the table.
This commit is contained in:
Tom Lane 2003-03-28 20:17:13 +00:00
parent bb3c00ee28
commit fd42262836
8 changed files with 104 additions and 73 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.174 2003/03/25 16:15:38 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.175 2003/03/28 20:17:13 tgl Exp $
-->
<Chapter Id="runtime">
@ -1599,7 +1599,7 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
<para>
It should be noted that the performance penalty of having
<varname>fsync</> on considerably less in
<varname>fsync</> on is considerably less in
<productname>PostgreSQL</> version 7.1 and later. If you
previously suppressed <function>fsync</> for performance
reasons, you may wish to reconsider your choice.
@ -2174,6 +2174,24 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ZERO_DAMAGED_PAGES</varname> (<type>boolean</type>)</term>
<listitem>
<para>
Detection of a damaged page header normally causes
<productname>PostgreSQL</> to report an error, aborting the current
transaction. Setting <varname>zero_damaged_pages</> to true causes
the system to instead report a warning, zero out the damaged page,
and continue processing. This behavior <emphasis>will lose data</>,
namely all the rows on the damaged page. But it allows you to get
past the error and retrieve rows from any undamaged pages that may
be present in the table. So it is useful for recovering data if
corruption has occurred due to hardware or software error. The
default setting is off, and it can only be changed by a superuser.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.134 2003/02/13 05:35:11 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.135 2003/03/28 20:17:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -49,6 +49,7 @@
#include "miscadmin.h"
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
#include "storage/bufpage.h"
#include "storage/proc.h"
#include "storage/smgr.h"
#include "utils/relcache.h"
@ -59,6 +60,10 @@
(*((XLogRecPtr*) MAKE_PTR((bufHdr)->data)))
/* GUC variable */
bool zero_damaged_pages = false;
static void WaitIO(BufferDesc *buf);
static void StartBufferIO(BufferDesc *buf, bool forInput);
static void TerminateBufferIO(BufferDesc *buf);
@ -217,6 +222,20 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
{
status = smgrread(DEFAULT_SMGR, reln, blockNum,
(char *) MAKE_PTR(bufHdr->data));
/* check for garbage data */
if (status == SM_SUCCESS &&
!PageHeaderIsValid((PageHeader) MAKE_PTR(bufHdr->data)))
{
if (zero_damaged_pages)
{
elog(WARNING, "Invalid page header in block %u of %s; zeroing out page",
blockNum, RelationGetRelationName(reln));
MemSet((char *) MAKE_PTR(bufHdr->data), 0, BLCKSZ);
}
else
elog(ERROR, "Invalid page header in block %u of %s",
blockNum, RelationGetRelationName(reln));
}
}
if (isLocalBuf)

View File

@ -8,14 +8,12 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.51 2003/01/11 05:01:03 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.52 2003/03/28 20:17:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <sys/file.h>
#include "storage/bufpage.h"
@ -48,6 +46,51 @@ PageInit(Page page, Size pageSize, Size specialSize)
}
/*
* PageHeaderIsValid
* Check that the header fields of a page appear valid.
*
* This is called when a page has just been read in from disk. The idea is
* to cheaply detect trashed pages before we go nuts following bogus item
* pointers, testing invalid transaction identifiers, etc.
*
* It turns out to be necessary to allow zeroed pages here too. Even though
* this routine is *not* called when deliberately adding a page to a relation,
* there are scenarios in which a zeroed page might be found in a table.
* (Example: a backend extends a relation, then crashes before it can write
* any WAL entry about the new page. The kernel will already have the
* zeroed page in the file, and it will stay that way after restart.) So we
* allow zeroed pages here, and are careful that the page access macros
* treat such a page as empty and without free space. Eventually, VACUUM
* will clean up such a page and make it usable.
*/
bool
PageHeaderIsValid(PageHeader page)
{
char *pagebytes;
int i;
/* Check normal case */
if (PageGetPageSize(page) == BLCKSZ &&
PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION &&
page->pd_lower >= SizeOfPageHeaderData &&
page->pd_lower <= page->pd_upper &&
page->pd_upper <= page->pd_special &&
page->pd_special <= BLCKSZ &&
page->pd_special == MAXALIGN(page->pd_special))
return true;
/* Check all-zeroes case */
pagebytes = (char *) page;
for (i = 0; i < BLCKSZ; i++)
{
if (pagebytes[i] != 0)
return false;
}
return true;
}
/* ----------------
* PageAddItem
*

View File

@ -5,7 +5,7 @@
* command, configuration file, and command line options.
* See src/backend/utils/misc/README for more information.
*
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.117 2003/03/20 04:51:44 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.118 2003/03/28 20:17:13 tgl Exp $
*
* Copyright 2000 by PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>.
@ -357,6 +357,10 @@ static struct config_bool
{"fsync", PGC_SIGHUP}, &enableFsync,
true, NULL, NULL
},
{
{"zero_damaged_pages", PGC_SUSET}, &zero_damaged_pages,
false, NULL, NULL
},
{
{"silent_mode", PGC_POSTMASTER}, &SilentMode,
false, NULL, NULL

View File

@ -213,6 +213,6 @@
#sql_inheritance = true
#transform_null_equals = false
#statement_timeout = 0 # 0 is disabled, in milliseconds
#zero_damaged_pages = false # set this true only for disaster recovery
#db_user_namespace = false
#preload_libraries = ''

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: bufmgr.h,v 1.66 2002/10/22 20:00:48 petere Exp $
* $Id: bufmgr.h,v 1.67 2003/03/28 20:17:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -25,6 +25,9 @@ typedef void *Block;
/* in globals.c ... this duplicates miscadmin.h */
extern DLLIMPORT int NBuffers;
/* in bufmgr.c */
extern bool zero_damaged_pages;
/* in buf_init.c */
extern DLLIMPORT Block *BufferBlockPointers;
extern long *PrivateRefCount;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: bufpage.h,v 1.53 2002/09/04 20:31:45 momjian Exp $
* $Id: bufpage.h,v 1.54 2003/03/28 20:17:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,7 +19,6 @@
#include "storage/item.h"
#include "storage/itemid.h"
#include "storage/off.h"
#include "storage/page.h"
#include "access/xlog.h"
/*
@ -74,11 +73,7 @@
* fields.
*/
/*
* PageIsValid
* True iff page is valid.
*/
#define PageIsValid(page) PointerIsValid(page)
typedef Pointer Page;
/*
@ -141,22 +136,12 @@ typedef PageHeaderData *PageHeader;
* page support macros
* ----------------------------------------------------------------
*/
/*
* PageIsValid -- This is defined in page.h.
*/
/*
* PageIsUsed
* True iff the page size is used.
*
* Note:
* Assumes page is valid.
* PageIsValid
* True iff page is valid.
*/
#define PageIsUsed(page) \
( \
AssertMacro(PageIsValid(page)), \
((bool) (((PageHeader) (page))->pd_lower != 0)) \
)
#define PageIsValid(page) PointerIsValid(page)
/*
* line pointer does not count as part of header
@ -172,9 +157,8 @@ typedef PageHeaderData *PageHeader;
/*
* PageIsNew
* returns true iff page is not initialized (by PageInit)
* returns true iff page has not been initialized (by PageInit)
*/
#define PageIsNew(page) (((PageHeader) (page))->pd_upper == 0)
/*
@ -199,12 +183,6 @@ typedef PageHeaderData *PageHeader;
/*
* PageSizeIsValid
* True iff the page size is valid.
*
* XXX currently all page sizes are "valid" but we only actually
* use BLCKSZ.
*
* 01/06/98 Now does something useful. darrenk
*
*/
#define PageSizeIsValid(pageSize) ((pageSize) == BLCKSZ)
@ -214,7 +192,7 @@ typedef PageHeaderData *PageHeader;
*
* this can only be called on a formatted page (unlike
* BufferGetPageSize, which can be called on an unformatted page).
* however, it can be called on a page for which there is no buffer.
* however, it can be called on a page that is not stored in a buffer.
*/
#define PageGetPageSize(page) \
((Size) (((PageHeader) (page))->pd_pagesize_version & (uint16) 0xFF00))
@ -222,10 +200,6 @@ typedef PageHeaderData *PageHeader;
/*
* PageGetPageLayoutVersion
* Returns the page layout version of a page.
*
* this can only be called on a formatted page (unlike
* BufferGetPageSize, which can be called on an unformatted page).
* however, it can be called on a page for which there is no buffer.
*/
#define PageGetPageLayoutVersion(page) \
(((PageHeader) (page))->pd_pagesize_version & 0x00FF)
@ -251,9 +225,6 @@ typedef PageHeaderData *PageHeader;
/*
* PageGetSpecialSize
* Returns size of special space on a page.
*
* Note:
* Assumes page is locked.
*/
#define PageGetSpecialSize(page) \
((uint16) (PageGetPageSize(page) - ((PageHeader)(page))->pd_special))
@ -261,9 +232,6 @@ typedef PageHeaderData *PageHeader;
/*
* PageGetSpecialPointer
* Returns pointer to special space on a page.
*
* Note:
* Assumes page is locked.
*/
#define PageGetSpecialPointer(page) \
( \
@ -339,6 +307,7 @@ typedef PageHeaderData *PageHeader;
*/
extern void PageInit(Page page, Size pageSize, Size specialSize);
extern bool PageHeaderIsValid(PageHeader page);
extern OffsetNumber PageAddItem(Page page, Item item, Size size,
OffsetNumber offsetNumber, ItemIdFlags flags);
extern Page PageGetTempPage(Page page, Size specialSize);

View File

@ -1,25 +0,0 @@
/*-------------------------------------------------------------------------
*
* page.h
* POSTGRES buffer page abstraction definitions.
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: page.h,v 1.12 2002/06/20 20:29:52 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PAGE_H
#define PAGE_H
typedef Pointer Page;
/*
* PageIsValid
* True iff page is valid.
*/
#define PageIsValid(page) PointerIsValid(page)
#endif /* PAGE_H */