diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c index d6296156cf..3e065eab97 100644 --- a/src/backend/storage/freespace/freespace.c +++ b/src/backend/storage/freespace/freespace.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/freespace/freespace.c,v 1.62 2008/09/30 14:15:58 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/freespace/freespace.c,v 1.63 2008/10/01 08:12:14 heikki Exp $ * * * NOTES: @@ -123,6 +123,8 @@ static int fsm_set_and_search(Relation rel, FSMAddress addr, uint16 slot, static BlockNumber fsm_search(Relation rel, uint8 min_cat); static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof); +static void fsm_redo_truncate(xl_fsm_truncate *xlrec); + /******** Public API ********/ @@ -281,7 +283,7 @@ FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks) * record, but that's not enough to zero out the last remaining FSM page. * (if we didn't need to zero out anything above, we can skip this) */ - if (!rel->rd_istemp && !InRecovery && first_removed_slot != 0) + if (!rel->rd_istemp && first_removed_slot != 0) { xl_fsm_truncate xlrec; XLogRecData rdata; @@ -310,8 +312,8 @@ FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks) * Need to invalidate the relcache entry, because rd_fsm_nblocks_cache * seen by other backends is no longer valid. */ - if (!InRecovery) - CacheInvalidateRelcache(rel); + CacheInvalidateRelcache(rel); + rel->rd_fsm_nblocks_cache = new_nfsmblocks; } @@ -762,6 +764,43 @@ fsm_vacuum_page(Relation rel, FSMAddress addr, bool *eof_p) /****** WAL-logging ******/ +static void +fsm_redo_truncate(xl_fsm_truncate *xlrec) +{ + FSMAddress first_removed_address; + uint16 first_removed_slot; + BlockNumber fsmblk; + Buffer buf; + + /* Get the location in the FSM of the first removed heap block */ + first_removed_address = fsm_get_location(xlrec->nheapblocks, + &first_removed_slot); + fsmblk = fsm_logical_to_physical(first_removed_address); + + /* + * Zero out the tail of the last remaining FSM page. We rely on the + * replay of the smgr truncation record to remove completely unused + * pages. + */ + buf = XLogReadBufferWithFork(xlrec->node, FSM_FORKNUM, fsmblk, false); + if (BufferIsValid(buf)) + { + fsm_truncate_avail(BufferGetPage(buf), first_removed_slot); + MarkBufferDirty(buf); + UnlockReleaseBuffer(buf); + } + else + { + /* + * The page doesn't exist. Because FSM extensions are not WAL-logged, + * it's normal to have a truncation record for a page that doesn't + * exist. Tell xlogutils.c not to PANIC at the end of recovery + * because of the missing page + */ + XLogTruncateRelation(xlrec->node, FSM_FORKNUM, fsmblk); + } +} + void fsm_redo(XLogRecPtr lsn, XLogRecord *record) { @@ -770,15 +809,7 @@ fsm_redo(XLogRecPtr lsn, XLogRecord *record) switch (info) { case XLOG_FSM_TRUNCATE: - { - xl_fsm_truncate *xlrec; - Relation rel; - - xlrec = (xl_fsm_truncate *) XLogRecGetData(record); - rel = CreateFakeRelcacheEntry(xlrec->node); - FreeSpaceMapTruncateRel(rel, xlrec->nheapblocks); - FreeFakeRelcacheEntry(rel); - } + fsm_redo_truncate((xl_fsm_truncate *) XLogRecGetData(record)); break; default: elog(PANIC, "fsm_redo: unknown op code %u", info);