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:
parent
bb3c00ee28
commit
fd42262836
|
@ -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>
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = ''
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
Loading…
Reference in New Issue