diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index cc6efab0cfc44bb23199e37d10763569e30bd00d..18dd9fe4a439f8e6ab81ab211985b3dda97d4c93 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -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> diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index a1abaf22c138cf0ab8c6218d7479bd867e3e65b5..119cfe92b0dafa1de23109fbda6cf671a56eec8e 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -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) diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c index fd10c2aa918d72c844e7eb2bf587a46991254d38..be024eb6a81011f980c2ca2433419a7a7fbc7c14 100644 --- a/src/backend/storage/page/bufpage.c +++ b/src/backend/storage/page/bufpage.c @@ -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 * diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 7c5d6d1884964f50bec140d773f58468b60937ea..89b625277971b3c660d327864d20ccb92f807aa1 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -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 diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index d14e9c78db821602fe1de414b496bcc555c1122f..ebf99187c670e4f2d79965e763f94e8ef76f6b89 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -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 = '' - diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index 5fcc354849df33ba6023e663f027b3c950648ff7..767530fc1891ecc4da3d271638fd3858bca5ade1 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -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; diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h index 159a82a0228a61e0998434d5bd84a8bcd4b782e3..0222d1dc0eda2cb3e69ec58e774d2c8421f04d45 100644 --- a/src/include/storage/bufpage.h +++ b/src/include/storage/bufpage.h @@ -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); diff --git a/src/include/storage/page.h b/src/include/storage/page.h deleted file mode 100644 index 6f2ad05e924119564af292056262f1b95bd00e4c..0000000000000000000000000000000000000000 --- a/src/include/storage/page.h +++ /dev/null @@ -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 */