Skip to content

Commit fd42262

Browse files
committed
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.
1 parent bb3c00e commit fd42262

File tree

8 files changed

+104
-73
lines changed

8 files changed

+104
-73
lines changed

doc/src/sgml/runtime.sgml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.174 2003/03/25 16:15:38 petere Exp $
2+
$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.175 2003/03/28 20:17:13 tgl Exp $
33
-->
44

55
<Chapter Id="runtime">
@@ -1599,7 +1599,7 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
15991599

16001600
<para>
16011601
It should be noted that the performance penalty of having
1602-
<varname>fsync</> on considerably less in
1602+
<varname>fsync</> on is considerably less in
16031603
<productname>PostgreSQL</> version 7.1 and later. If you
16041604
previously suppressed <function>fsync</> for performance
16051605
reasons, you may wish to reconsider your choice.
@@ -2174,6 +2174,24 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
21742174
</listitem>
21752175
</varlistentry>
21762176

2177+
<varlistentry>
2178+
<term><varname>ZERO_DAMAGED_PAGES</varname> (<type>boolean</type>)</term>
2179+
<listitem>
2180+
<para>
2181+
Detection of a damaged page header normally causes
2182+
<productname>PostgreSQL</> to report an error, aborting the current
2183+
transaction. Setting <varname>zero_damaged_pages</> to true causes
2184+
the system to instead report a warning, zero out the damaged page,
2185+
and continue processing. This behavior <emphasis>will lose data</>,
2186+
namely all the rows on the damaged page. But it allows you to get
2187+
past the error and retrieve rows from any undamaged pages that may
2188+
be present in the table. So it is useful for recovering data if
2189+
corruption has occurred due to hardware or software error. The
2190+
default setting is off, and it can only be changed by a superuser.
2191+
</para>
2192+
</listitem>
2193+
</varlistentry>
2194+
21772195
</variablelist>
21782196
</sect2>
21792197

src/backend/storage/buffer/bufmgr.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.134 2003/02/13 05:35:11 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.135 2003/03/28 20:17:13 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -49,6 +49,7 @@
4949
#include "miscadmin.h"
5050
#include "storage/buf_internals.h"
5151
#include "storage/bufmgr.h"
52+
#include "storage/bufpage.h"
5253
#include "storage/proc.h"
5354
#include "storage/smgr.h"
5455
#include "utils/relcache.h"
@@ -59,6 +60,10 @@
5960
(*((XLogRecPtr*) MAKE_PTR((bufHdr)->data)))
6061

6162

63+
/* GUC variable */
64+
bool zero_damaged_pages = false;
65+
66+
6267
static void WaitIO(BufferDesc *buf);
6368
static void StartBufferIO(BufferDesc *buf, bool forInput);
6469
static void TerminateBufferIO(BufferDesc *buf);
@@ -217,6 +222,20 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
217222
{
218223
status = smgrread(DEFAULT_SMGR, reln, blockNum,
219224
(char *) MAKE_PTR(bufHdr->data));
225+
/* check for garbage data */
226+
if (status == SM_SUCCESS &&
227+
!PageHeaderIsValid((PageHeader) MAKE_PTR(bufHdr->data)))
228+
{
229+
if (zero_damaged_pages)
230+
{
231+
elog(WARNING, "Invalid page header in block %u of %s; zeroing out page",
232+
blockNum, RelationGetRelationName(reln));
233+
MemSet((char *) MAKE_PTR(bufHdr->data), 0, BLCKSZ);
234+
}
235+
else
236+
elog(ERROR, "Invalid page header in block %u of %s",
237+
blockNum, RelationGetRelationName(reln));
238+
}
220239
}
221240

222241
if (isLocalBuf)

src/backend/storage/page/bufpage.c

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.51 2003/01/11 05:01:03 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.52 2003/03/28 20:17:13 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
1515
#include "postgres.h"
1616

17-
#include <sys/file.h>
18-
1917
#include "storage/bufpage.h"
2018

2119

@@ -48,6 +46,51 @@ PageInit(Page page, Size pageSize, Size specialSize)
4846
}
4947

5048

49+
/*
50+
* PageHeaderIsValid
51+
* Check that the header fields of a page appear valid.
52+
*
53+
* This is called when a page has just been read in from disk. The idea is
54+
* to cheaply detect trashed pages before we go nuts following bogus item
55+
* pointers, testing invalid transaction identifiers, etc.
56+
*
57+
* It turns out to be necessary to allow zeroed pages here too. Even though
58+
* this routine is *not* called when deliberately adding a page to a relation,
59+
* there are scenarios in which a zeroed page might be found in a table.
60+
* (Example: a backend extends a relation, then crashes before it can write
61+
* any WAL entry about the new page. The kernel will already have the
62+
* zeroed page in the file, and it will stay that way after restart.) So we
63+
* allow zeroed pages here, and are careful that the page access macros
64+
* treat such a page as empty and without free space. Eventually, VACUUM
65+
* will clean up such a page and make it usable.
66+
*/
67+
bool
68+
PageHeaderIsValid(PageHeader page)
69+
{
70+
char *pagebytes;
71+
int i;
72+
73+
/* Check normal case */
74+
if (PageGetPageSize(page) == BLCKSZ &&
75+
PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION &&
76+
page->pd_lower >= SizeOfPageHeaderData &&
77+
page->pd_lower <= page->pd_upper &&
78+
page->pd_upper <= page->pd_special &&
79+
page->pd_special <= BLCKSZ &&
80+
page->pd_special == MAXALIGN(page->pd_special))
81+
return true;
82+
83+
/* Check all-zeroes case */
84+
pagebytes = (char *) page;
85+
for (i = 0; i < BLCKSZ; i++)
86+
{
87+
if (pagebytes[i] != 0)
88+
return false;
89+
}
90+
return true;
91+
}
92+
93+
5194
/* ----------------
5295
* PageAddItem
5396
*

src/backend/utils/misc/guc.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* command, configuration file, and command line options.
66
* See src/backend/utils/misc/README for more information.
77
*
8-
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.117 2003/03/20 04:51:44 momjian Exp $
8+
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.118 2003/03/28 20:17:13 tgl Exp $
99
*
1010
* Copyright 2000 by PostgreSQL Global Development Group
1111
* Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -357,6 +357,10 @@ static struct config_bool
357357
{"fsync", PGC_SIGHUP}, &enableFsync,
358358
true, NULL, NULL
359359
},
360+
{
361+
{"zero_damaged_pages", PGC_SUSET}, &zero_damaged_pages,
362+
false, NULL, NULL
363+
},
360364
{
361365
{"silent_mode", PGC_POSTMASTER}, &SilentMode,
362366
false, NULL, NULL

src/backend/utils/misc/postgresql.conf.sample

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,6 @@
213213
#sql_inheritance = true
214214
#transform_null_equals = false
215215
#statement_timeout = 0 # 0 is disabled, in milliseconds
216+
#zero_damaged_pages = false # set this true only for disaster recovery
216217
#db_user_namespace = false
217218
#preload_libraries = ''
218-

src/include/storage/bufmgr.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $Id: bufmgr.h,v 1.66 2002/10/22 20:00:48 petere Exp $
10+
* $Id: bufmgr.h,v 1.67 2003/03/28 20:17:13 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -25,6 +25,9 @@ typedef void *Block;
2525
/* in globals.c ... this duplicates miscadmin.h */
2626
extern DLLIMPORT int NBuffers;
2727

28+
/* in bufmgr.c */
29+
extern bool zero_damaged_pages;
30+
2831
/* in buf_init.c */
2932
extern DLLIMPORT Block *BufferBlockPointers;
3033
extern long *PrivateRefCount;

src/include/storage/bufpage.h

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $Id: bufpage.h,v 1.53 2002/09/04 20:31:45 momjian Exp $
10+
* $Id: bufpage.h,v 1.54 2003/03/28 20:17:13 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -19,7 +19,6 @@
1919
#include "storage/item.h"
2020
#include "storage/itemid.h"
2121
#include "storage/off.h"
22-
#include "storage/page.h"
2322
#include "access/xlog.h"
2423

2524
/*
@@ -74,11 +73,7 @@
7473
* fields.
7574
*/
7675

77-
/*
78-
* PageIsValid
79-
* True iff page is valid.
80-
*/
81-
#define PageIsValid(page) PointerIsValid(page)
76+
typedef Pointer Page;
8277

8378

8479
/*
@@ -141,22 +136,12 @@ typedef PageHeaderData *PageHeader;
141136
* page support macros
142137
* ----------------------------------------------------------------
143138
*/
144-
/*
145-
* PageIsValid -- This is defined in page.h.
146-
*/
147139

148140
/*
149-
* PageIsUsed
150-
* True iff the page size is used.
151-
*
152-
* Note:
153-
* Assumes page is valid.
141+
* PageIsValid
142+
* True iff page is valid.
154143
*/
155-
#define PageIsUsed(page) \
156-
( \
157-
AssertMacro(PageIsValid(page)), \
158-
((bool) (((PageHeader) (page))->pd_lower != 0)) \
159-
)
144+
#define PageIsValid(page) PointerIsValid(page)
160145

161146
/*
162147
* line pointer does not count as part of header
@@ -172,9 +157,8 @@ typedef PageHeaderData *PageHeader;
172157

173158
/*
174159
* PageIsNew
175-
* returns true iff page is not initialized (by PageInit)
160+
* returns true iff page has not been initialized (by PageInit)
176161
*/
177-
178162
#define PageIsNew(page) (((PageHeader) (page))->pd_upper == 0)
179163

180164
/*
@@ -199,12 +183,6 @@ typedef PageHeaderData *PageHeader;
199183
/*
200184
* PageSizeIsValid
201185
* True iff the page size is valid.
202-
*
203-
* XXX currently all page sizes are "valid" but we only actually
204-
* use BLCKSZ.
205-
*
206-
* 01/06/98 Now does something useful. darrenk
207-
*
208186
*/
209187
#define PageSizeIsValid(pageSize) ((pageSize) == BLCKSZ)
210188

@@ -214,18 +192,14 @@ typedef PageHeaderData *PageHeader;
214192
*
215193
* this can only be called on a formatted page (unlike
216194
* BufferGetPageSize, which can be called on an unformatted page).
217-
* however, it can be called on a page for which there is no buffer.
195+
* however, it can be called on a page that is not stored in a buffer.
218196
*/
219197
#define PageGetPageSize(page) \
220198
((Size) (((PageHeader) (page))->pd_pagesize_version & (uint16) 0xFF00))
221199

222200
/*
223201
* PageGetPageLayoutVersion
224202
* Returns the page layout version of a page.
225-
*
226-
* this can only be called on a formatted page (unlike
227-
* BufferGetPageSize, which can be called on an unformatted page).
228-
* however, it can be called on a page for which there is no buffer.
229203
*/
230204
#define PageGetPageLayoutVersion(page) \
231205
(((PageHeader) (page))->pd_pagesize_version & 0x00FF)
@@ -251,19 +225,13 @@ typedef PageHeaderData *PageHeader;
251225
/*
252226
* PageGetSpecialSize
253227
* Returns size of special space on a page.
254-
*
255-
* Note:
256-
* Assumes page is locked.
257228
*/
258229
#define PageGetSpecialSize(page) \
259230
((uint16) (PageGetPageSize(page) - ((PageHeader)(page))->pd_special))
260231

261232
/*
262233
* PageGetSpecialPointer
263234
* Returns pointer to special space on a page.
264-
*
265-
* Note:
266-
* Assumes page is locked.
267235
*/
268236
#define PageGetSpecialPointer(page) \
269237
( \
@@ -339,6 +307,7 @@ typedef PageHeaderData *PageHeader;
339307
*/
340308

341309
extern void PageInit(Page page, Size pageSize, Size specialSize);
310+
extern bool PageHeaderIsValid(PageHeader page);
342311
extern OffsetNumber PageAddItem(Page page, Item item, Size size,
343312
OffsetNumber offsetNumber, ItemIdFlags flags);
344313
extern Page PageGetTempPage(Page page, Size specialSize);

src/include/storage/page.h

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)