00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "postgres.h"
00019
00020 #include "access/xlog.h"
00021 #include "access/xlogutils.h"
00022 #include "catalog/catalog.h"
00023 #include "common/relpath.h"
00024 #include "storage/smgr.h"
00025 #include "utils/guc.h"
00026 #include "utils/hsearch.h"
00027 #include "utils/rel.h"
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 typedef struct xl_invalid_page_key
00041 {
00042 RelFileNode node;
00043 ForkNumber forkno;
00044 BlockNumber blkno;
00045 } xl_invalid_page_key;
00046
00047 typedef struct xl_invalid_page
00048 {
00049 xl_invalid_page_key key;
00050 bool present;
00051 } xl_invalid_page;
00052
00053 static HTAB *invalid_page_tab = NULL;
00054
00055
00056
00057 static void
00058 report_invalid_page(int elevel, RelFileNode node, ForkNumber forkno,
00059 BlockNumber blkno, bool present)
00060 {
00061 char *path = relpathperm(node, forkno);
00062
00063 if (present)
00064 elog(elevel, "page %u of relation %s is uninitialized",
00065 blkno, path);
00066 else
00067 elog(elevel, "page %u of relation %s does not exist",
00068 blkno, path);
00069 pfree(path);
00070 }
00071
00072
00073 static void
00074 log_invalid_page(RelFileNode node, ForkNumber forkno, BlockNumber blkno,
00075 bool present)
00076 {
00077 xl_invalid_page_key key;
00078 xl_invalid_page *hentry;
00079 bool found;
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089 if (reachedConsistency)
00090 {
00091 report_invalid_page(WARNING, node, forkno, blkno, present);
00092 elog(PANIC, "WAL contains references to invalid pages");
00093 }
00094
00095
00096
00097
00098
00099
00100 if (log_min_messages <= DEBUG1 || client_min_messages <= DEBUG1)
00101 report_invalid_page(DEBUG1, node, forkno, blkno, present);
00102
00103 if (invalid_page_tab == NULL)
00104 {
00105
00106 HASHCTL ctl;
00107
00108 memset(&ctl, 0, sizeof(ctl));
00109 ctl.keysize = sizeof(xl_invalid_page_key);
00110 ctl.entrysize = sizeof(xl_invalid_page);
00111 ctl.hash = tag_hash;
00112
00113 invalid_page_tab = hash_create("XLOG invalid-page table",
00114 100,
00115 &ctl,
00116 HASH_ELEM | HASH_FUNCTION);
00117 }
00118
00119
00120 key.node = node;
00121 key.forkno = forkno;
00122 key.blkno = blkno;
00123 hentry = (xl_invalid_page *)
00124 hash_search(invalid_page_tab, (void *) &key, HASH_ENTER, &found);
00125
00126 if (!found)
00127 {
00128
00129 hentry->present = present;
00130 }
00131 else
00132 {
00133
00134 }
00135 }
00136
00137
00138 static void
00139 forget_invalid_pages(RelFileNode node, ForkNumber forkno, BlockNumber minblkno)
00140 {
00141 HASH_SEQ_STATUS status;
00142 xl_invalid_page *hentry;
00143
00144 if (invalid_page_tab == NULL)
00145 return;
00146
00147 hash_seq_init(&status, invalid_page_tab);
00148
00149 while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL)
00150 {
00151 if (RelFileNodeEquals(hentry->key.node, node) &&
00152 hentry->key.forkno == forkno &&
00153 hentry->key.blkno >= minblkno)
00154 {
00155 if (log_min_messages <= DEBUG2 || client_min_messages <= DEBUG2)
00156 {
00157 char *path = relpathperm(hentry->key.node, forkno);
00158
00159 elog(DEBUG2, "page %u of relation %s has been dropped",
00160 hentry->key.blkno, path);
00161 pfree(path);
00162 }
00163
00164 if (hash_search(invalid_page_tab,
00165 (void *) &hentry->key,
00166 HASH_REMOVE, NULL) == NULL)
00167 elog(ERROR, "hash table corrupted");
00168 }
00169 }
00170 }
00171
00172
00173 static void
00174 forget_invalid_pages_db(Oid dbid)
00175 {
00176 HASH_SEQ_STATUS status;
00177 xl_invalid_page *hentry;
00178
00179 if (invalid_page_tab == NULL)
00180 return;
00181
00182 hash_seq_init(&status, invalid_page_tab);
00183
00184 while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL)
00185 {
00186 if (hentry->key.node.dbNode == dbid)
00187 {
00188 if (log_min_messages <= DEBUG2 || client_min_messages <= DEBUG2)
00189 {
00190 char *path = relpathperm(hentry->key.node, hentry->key.forkno);
00191
00192 elog(DEBUG2, "page %u of relation %s has been dropped",
00193 hentry->key.blkno, path);
00194 pfree(path);
00195 }
00196
00197 if (hash_search(invalid_page_tab,
00198 (void *) &hentry->key,
00199 HASH_REMOVE, NULL) == NULL)
00200 elog(ERROR, "hash table corrupted");
00201 }
00202 }
00203 }
00204
00205
00206 bool
00207 XLogHaveInvalidPages(void)
00208 {
00209 if (invalid_page_tab != NULL &&
00210 hash_get_num_entries(invalid_page_tab) > 0)
00211 return true;
00212 return false;
00213 }
00214
00215
00216 void
00217 XLogCheckInvalidPages(void)
00218 {
00219 HASH_SEQ_STATUS status;
00220 xl_invalid_page *hentry;
00221 bool foundone = false;
00222
00223 if (invalid_page_tab == NULL)
00224 return;
00225
00226 hash_seq_init(&status, invalid_page_tab);
00227
00228
00229
00230
00231
00232 while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL)
00233 {
00234 report_invalid_page(WARNING, hentry->key.node, hentry->key.forkno,
00235 hentry->key.blkno, hentry->present);
00236 foundone = true;
00237 }
00238
00239 if (foundone)
00240 elog(PANIC, "WAL contains references to invalid pages");
00241
00242 hash_destroy(invalid_page_tab);
00243 invalid_page_tab = NULL;
00244 }
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263 Buffer
00264 XLogReadBuffer(RelFileNode rnode, BlockNumber blkno, bool init)
00265 {
00266 Buffer buf;
00267
00268 buf = XLogReadBufferExtended(rnode, MAIN_FORKNUM, blkno,
00269 init ? RBM_ZERO : RBM_NORMAL);
00270 if (BufferIsValid(buf))
00271 LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
00272
00273 return buf;
00274 }
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292 Buffer
00293 XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
00294 BlockNumber blkno, ReadBufferMode mode)
00295 {
00296 BlockNumber lastblock;
00297 Buffer buffer;
00298 SMgrRelation smgr;
00299
00300 Assert(blkno != P_NEW);
00301
00302
00303 smgr = smgropen(rnode, InvalidBackendId);
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313 smgrcreate(smgr, forknum, true);
00314
00315 lastblock = smgrnblocks(smgr, forknum);
00316
00317 if (blkno < lastblock)
00318 {
00319
00320 buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
00321 mode, NULL);
00322 }
00323 else
00324 {
00325
00326 if (mode == RBM_NORMAL)
00327 {
00328 log_invalid_page(rnode, forknum, blkno, false);
00329 return InvalidBuffer;
00330 }
00331
00332
00333 Assert(InRecovery);
00334 buffer = InvalidBuffer;
00335 while (blkno >= lastblock)
00336 {
00337 if (buffer != InvalidBuffer)
00338 ReleaseBuffer(buffer);
00339 buffer = ReadBufferWithoutRelcache(rnode, forknum,
00340 P_NEW, mode, NULL);
00341 lastblock++;
00342 }
00343 Assert(BufferGetBlockNumber(buffer) == blkno);
00344 }
00345
00346 if (mode == RBM_NORMAL)
00347 {
00348
00349 Page page = (Page) BufferGetPage(buffer);
00350
00351
00352
00353
00354
00355
00356 if (PageIsNew(page))
00357 {
00358 ReleaseBuffer(buffer);
00359 log_invalid_page(rnode, forknum, blkno, true);
00360 return InvalidBuffer;
00361 }
00362 }
00363
00364 return buffer;
00365 }
00366
00367
00368
00369
00370
00371
00372 typedef struct
00373 {
00374 RelationData reldata;
00375 FormData_pg_class pgc;
00376 } FakeRelCacheEntryData;
00377
00378 typedef FakeRelCacheEntryData *FakeRelCacheEntry;
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392 Relation
00393 CreateFakeRelcacheEntry(RelFileNode rnode)
00394 {
00395 FakeRelCacheEntry fakeentry;
00396 Relation rel;
00397
00398 Assert(InRecovery);
00399
00400
00401 fakeentry = palloc0(sizeof(FakeRelCacheEntryData));
00402 rel = (Relation) fakeentry;
00403
00404 rel->rd_rel = &fakeentry->pgc;
00405 rel->rd_node = rnode;
00406
00407 rel->rd_backend = InvalidBackendId;
00408
00409
00410 rel->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
00411
00412
00413 sprintf(RelationGetRelationName(rel), "%u", rnode.relNode);
00414
00415
00416
00417
00418
00419
00420
00421
00422 rel->rd_lockInfo.lockRelId.dbId = rnode.dbNode;
00423 rel->rd_lockInfo.lockRelId.relId = rnode.relNode;
00424
00425 rel->rd_smgr = NULL;
00426
00427 return rel;
00428 }
00429
00430
00431
00432
00433 void
00434 FreeFakeRelcacheEntry(Relation fakerel)
00435 {
00436 pfree(fakerel);
00437 }
00438
00439
00440
00441
00442
00443
00444
00445 void
00446 XLogDropRelation(RelFileNode rnode, ForkNumber forknum)
00447 {
00448 forget_invalid_pages(rnode, forknum, 0);
00449 }
00450
00451
00452
00453
00454
00455
00456 void
00457 XLogDropDatabase(Oid dbid)
00458 {
00459
00460
00461
00462
00463
00464
00465 smgrcloseall();
00466
00467 forget_invalid_pages_db(dbid);
00468 }
00469
00470
00471
00472
00473
00474
00475 void
00476 XLogTruncateRelation(RelFileNode rnode, ForkNumber forkNum,
00477 BlockNumber nblocks)
00478 {
00479 forget_invalid_pages(rnode, forkNum, nblocks);
00480 }