#include "postgres.h"
#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/xlogutils.h"
#include "catalog/catalog.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "storage/freespace.h"
#include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Go to the source code of this file.
Data Structures | |
struct | PendingRelDelete |
Typedefs | |
typedef struct PendingRelDelete | PendingRelDelete |
Functions | |
void | RelationCreateStorage (RelFileNode rnode, char relpersistence) |
void | log_smgrcreate (RelFileNode *rnode, ForkNumber forkNum) |
void | RelationDropStorage (Relation rel) |
void | RelationPreserveStorage (RelFileNode rnode, bool atCommit) |
void | RelationTruncate (Relation rel, BlockNumber nblocks) |
void | smgrDoPendingDeletes (bool isCommit) |
int | smgrGetPendingDeletes (bool forCommit, RelFileNode **ptr) |
void | PostPrepare_smgr (void) |
void | AtSubCommit_smgr (void) |
void | AtSubAbort_smgr (void) |
void | smgr_redo (XLogRecPtr lsn, XLogRecord *record) |
Variables | |
static PendingRelDelete * | pendingDeletes = NULL |
typedef struct PendingRelDelete PendingRelDelete |
void AtSubAbort_smgr | ( | void | ) |
Definition at line 470 of file storage.c.
References smgrDoPendingDeletes().
Referenced by AbortSubTransaction().
{ smgrDoPendingDeletes(false); }
void AtSubCommit_smgr | ( | void | ) |
Definition at line 450 of file storage.c.
References GetCurrentTransactionNestLevel(), PendingRelDelete::nestLevel, and PendingRelDelete::next.
Referenced by CommitSubTransaction().
{ int nestLevel = GetCurrentTransactionNestLevel(); PendingRelDelete *pending; for (pending = pendingDeletes; pending != NULL; pending = pending->next) { if (pending->nestLevel >= nestLevel) pending->nestLevel = nestLevel - 1; } }
void log_smgrcreate | ( | RelFileNode * | rnode, | |
ForkNumber | forkNum | |||
) |
Definition at line 123 of file storage.c.
References XLogRecData::buffer, XLogRecData::data, xl_smgr_create::forkNum, XLogRecData::len, XLogRecData::next, xl_smgr_create::rnode, XLOG_SMGR_CREATE, and XLogInsert().
Referenced by heap_create_init_fork(), and RelationCreateStorage().
{ xl_smgr_create xlrec; XLogRecData rdata; /* * Make an XLOG entry reporting the file creation. */ xlrec.rnode = *rnode; xlrec.forkNum = forkNum; rdata.data = (char *) &xlrec; rdata.len = sizeof(xlrec); rdata.buffer = InvalidBuffer; rdata.next = NULL; XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE, &rdata); }
void PostPrepare_smgr | ( | void | ) |
Definition at line 429 of file storage.c.
References PendingRelDelete::next, and pfree().
Referenced by PrepareTransaction().
{ PendingRelDelete *pending; PendingRelDelete *next; for (pending = pendingDeletes; pending != NULL; pending = next) { next = pending->next; pendingDeletes = next; /* must explicitly free the list entry */ pfree(pending); } }
void RelationCreateStorage | ( | RelFileNode | rnode, | |
char | relpersistence | |||
) |
Definition at line 76 of file storage.c.
References PendingRelDelete::atCommit, PendingRelDelete::backend, elog, ERROR, GetCurrentTransactionNestLevel(), log_smgrcreate(), MAIN_FORKNUM, MemoryContextAlloc(), MyBackendId, PendingRelDelete::nestLevel, PendingRelDelete::next, RelFileNodeBackend::node, PendingRelDelete::relnode, RELPERSISTENCE_PERMANENT, RELPERSISTENCE_TEMP, RELPERSISTENCE_UNLOGGED, SMgrRelationData::smgr_rnode, smgrcreate(), smgropen(), and TopMemoryContext.
Referenced by ATExecSetTableSpace(), heap_create(), and RelationSetNewRelfilenode().
{ PendingRelDelete *pending; SMgrRelation srel; BackendId backend; bool needs_wal; switch (relpersistence) { case RELPERSISTENCE_TEMP: backend = MyBackendId; needs_wal = false; break; case RELPERSISTENCE_UNLOGGED: backend = InvalidBackendId; needs_wal = false; break; case RELPERSISTENCE_PERMANENT: backend = InvalidBackendId; needs_wal = true; break; default: elog(ERROR, "invalid relpersistence: %c", relpersistence); return; /* placate compiler */ } srel = smgropen(rnode, backend); smgrcreate(srel, MAIN_FORKNUM, false); if (needs_wal) log_smgrcreate(&srel->smgr_rnode.node, MAIN_FORKNUM); /* Add the relation to the list of stuff to delete at abort */ pending = (PendingRelDelete *) MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); pending->relnode = rnode; pending->backend = backend; pending->atCommit = false; /* delete if abort */ pending->nestLevel = GetCurrentTransactionNestLevel(); pending->next = pendingDeletes; pendingDeletes = pending; }
void RelationDropStorage | ( | Relation | rel | ) |
Definition at line 147 of file storage.c.
References PendingRelDelete::atCommit, PendingRelDelete::backend, GetCurrentTransactionNestLevel(), MemoryContextAlloc(), PendingRelDelete::nestLevel, PendingRelDelete::next, RelationData::rd_backend, RelationData::rd_node, RelationCloseSmgr, PendingRelDelete::relnode, and TopMemoryContext.
Referenced by ATExecSetTableSpace(), DefineQueryRewrite(), heap_drop_with_catalog(), index_drop(), and RelationSetNewRelfilenode().
{ PendingRelDelete *pending; /* Add the relation to the list of stuff to delete at commit */ pending = (PendingRelDelete *) MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); pending->relnode = rel->rd_node; pending->backend = rel->rd_backend; pending->atCommit = true; /* delete if commit */ pending->nestLevel = GetCurrentTransactionNestLevel(); pending->next = pendingDeletes; pendingDeletes = pending; /* * NOTE: if the relation was created in this transaction, it will now be * present in the pending-delete list twice, once with atCommit true and * once with atCommit false. Hence, it will be physically deleted at end * of xact in either case (and the other entry will be ignored by * smgrDoPendingDeletes, so no error will occur). We could instead remove * the existing list entry and delete the physical file immediately, but * for now I'll keep the logic simple. */ RelationCloseSmgr(rel); }
void RelationPreserveStorage | ( | RelFileNode | rnode, | |
bool | atCommit | |||
) |
Definition at line 192 of file storage.c.
References PendingRelDelete::atCommit, PendingRelDelete::next, pfree(), RelFileNodeEquals, and PendingRelDelete::relnode.
Referenced by ATExecAddIndex(), and write_relmap_file().
{ PendingRelDelete *pending; PendingRelDelete *prev; PendingRelDelete *next; prev = NULL; for (pending = pendingDeletes; pending != NULL; pending = next) { next = pending->next; if (RelFileNodeEquals(rnode, pending->relnode) && pending->atCommit == atCommit) { /* unlink and delete list entry */ if (prev) prev->next = next; else pendingDeletes = next; pfree(pending); /* prev does not change */ } else { /* unrelated entry, don't touch it */ prev = pending; } } }
void RelationTruncate | ( | Relation | rel, | |
BlockNumber | nblocks | |||
) |
Definition at line 229 of file storage.c.
References xl_smgr_truncate::blkno, XLogRecData::buffer, XLogRecData::data, FreeSpaceMapTruncateRel(), FSM_FORKNUM, XLogRecData::len, MAIN_FORKNUM, XLogRecData::next, RelationData::rd_node, RelationData::rd_smgr, RelationNeedsWAL, RelationOpenSmgr, xl_smgr_truncate::rnode, SMgrRelationData::smgr_fsm_nblocks, SMgrRelationData::smgr_targblock, SMgrRelationData::smgr_vm_nblocks, smgrexists(), smgrtruncate(), VISIBILITYMAP_FORKNUM, visibilitymap_truncate(), XLOG_SMGR_TRUNCATE, XLogFlush(), and XLogInsert().
Referenced by heap_truncate_one_rel(), lazy_truncate_heap(), RelationTruncateIndexes(), and spgvacuumscan().
{ bool fsm; bool vm; /* Open it at the smgr level if not already done */ RelationOpenSmgr(rel); /* * Make sure smgr_targblock etc aren't pointing somewhere past new end */ rel->rd_smgr->smgr_targblock = InvalidBlockNumber; rel->rd_smgr->smgr_fsm_nblocks = InvalidBlockNumber; rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber; /* Truncate the FSM first if it exists */ fsm = smgrexists(rel->rd_smgr, FSM_FORKNUM); if (fsm) FreeSpaceMapTruncateRel(rel, nblocks); /* Truncate the visibility map too if it exists. */ vm = smgrexists(rel->rd_smgr, VISIBILITYMAP_FORKNUM); if (vm) visibilitymap_truncate(rel, nblocks); /* * We WAL-log the truncation before actually truncating, which means * trouble if the truncation fails. If we then crash, the WAL replay * likely isn't going to succeed in the truncation either, and cause a * PANIC. It's tempting to put a critical section here, but that cure * would be worse than the disease. It would turn a usually harmless * failure to truncate, that might spell trouble at WAL replay, into a * certain PANIC. */ if (RelationNeedsWAL(rel)) { /* * Make an XLOG entry reporting the file truncation. */ XLogRecPtr lsn; XLogRecData rdata; xl_smgr_truncate xlrec; xlrec.blkno = nblocks; xlrec.rnode = rel->rd_node; rdata.data = (char *) &xlrec; rdata.len = sizeof(xlrec); rdata.buffer = InvalidBuffer; rdata.next = NULL; lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_TRUNCATE, &rdata); /* * Flush, because otherwise the truncation of the main relation might * hit the disk before the WAL record, and the truncation of the FSM * or visibility map. If we crashed during that window, we'd be left * with a truncated heap, but the FSM or visibility map would still * contain entries for the non-existent heap pages. */ if (fsm || vm) XLogFlush(lsn); } /* Do the real work */ smgrtruncate(rel->rd_smgr, MAIN_FORKNUM, nblocks); }
void smgr_redo | ( | XLogRecPtr | lsn, | |
XLogRecord * | record | |||
) |
Definition at line 476 of file storage.c.
References Assert, xl_smgr_truncate::blkno, CreateFakeRelcacheEntry(), elog, xl_smgr_create::forkNum, FreeFakeRelcacheEntry(), FreeSpaceMapTruncateRel(), FSM_FORKNUM, InvalidBackendId, MAIN_FORKNUM, PANIC, xl_smgr_truncate::rnode, xl_smgr_create::rnode, smgrcreate(), smgrexists(), smgropen(), smgrtruncate(), VISIBILITYMAP_FORKNUM, visibilitymap_truncate(), XLogRecord::xl_info, XLOG_SMGR_CREATE, XLOG_SMGR_TRUNCATE, XLogFlush(), XLogRecGetData, XLogTruncateRelation(), and XLR_BKP_BLOCK_MASK.
{ uint8 info = record->xl_info & ~XLR_INFO_MASK; /* Backup blocks are not used in smgr records */ Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK)); if (info == XLOG_SMGR_CREATE) { xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record); SMgrRelation reln; reln = smgropen(xlrec->rnode, InvalidBackendId); smgrcreate(reln, xlrec->forkNum, true); } else if (info == XLOG_SMGR_TRUNCATE) { xl_smgr_truncate *xlrec = (xl_smgr_truncate *) XLogRecGetData(record); SMgrRelation reln; Relation rel; reln = smgropen(xlrec->rnode, InvalidBackendId); /* * Forcibly create relation if it doesn't exist (which suggests that * it was dropped somewhere later in the WAL sequence). As in * XLogReadBuffer, we prefer to recreate the rel and replay the log as * best we can until the drop is seen. */ smgrcreate(reln, MAIN_FORKNUM, true); /* * Before we perform the truncation, update minimum recovery point * to cover this WAL record. Once the relation is truncated, there's * no going back. The buffer manager enforces the WAL-first rule * for normal updates to relation files, so that the minimum recovery * point is always updated before the corresponding change in the * data file is flushed to disk. We have to do the same manually * here. * * Doing this before the truncation means that if the truncation fails * for some reason, you cannot start up the system even after restart, * until you fix the underlying situation so that the truncation will * succeed. Alternatively, we could update the minimum recovery point * after truncation, but that would leave a small window where the * WAL-first rule could be violated. */ XLogFlush(lsn); smgrtruncate(reln, MAIN_FORKNUM, xlrec->blkno); /* Also tell xlogutils.c about it */ XLogTruncateRelation(xlrec->rnode, MAIN_FORKNUM, xlrec->blkno); /* Truncate FSM and VM too */ rel = CreateFakeRelcacheEntry(xlrec->rnode); if (smgrexists(reln, FSM_FORKNUM)) FreeSpaceMapTruncateRel(rel, xlrec->blkno); if (smgrexists(reln, VISIBILITYMAP_FORKNUM)) visibilitymap_truncate(rel, xlrec->blkno); FreeFakeRelcacheEntry(rel); } else elog(PANIC, "smgr_redo: unknown op code %u", info); }
void smgrDoPendingDeletes | ( | bool | isCommit | ) |
Definition at line 309 of file storage.c.
References PendingRelDelete::atCommit, PendingRelDelete::backend, GetCurrentTransactionNestLevel(), i, PendingRelDelete::nestLevel, PendingRelDelete::next, palloc(), pfree(), PendingRelDelete::relnode, repalloc(), smgrclose(), smgrdounlinkall(), and smgropen().
Referenced by AbortTransaction(), AtSubAbort_smgr(), and CommitTransaction().
{ int nestLevel = GetCurrentTransactionNestLevel(); PendingRelDelete *pending; PendingRelDelete *prev; PendingRelDelete *next; int nrels = 0, i = 0, maxrels = 8; SMgrRelation *srels = palloc(maxrels * sizeof(SMgrRelation)); prev = NULL; for (pending = pendingDeletes; pending != NULL; pending = next) { next = pending->next; if (pending->nestLevel < nestLevel) { /* outer-level entries should not be processed yet */ prev = pending; } else { /* unlink list entry first, so we don't retry on failure */ if (prev) prev->next = next; else pendingDeletes = next; /* do deletion if called for */ if (pending->atCommit == isCommit) { SMgrRelation srel; srel = smgropen(pending->relnode, pending->backend); /* extend the array if needed (double the size) */ if (maxrels <= nrels) { maxrels *= 2; srels = repalloc(srels, sizeof(SMgrRelation) * maxrels); } srels[nrels++] = srel; } /* must explicitly free the list entry */ pfree(pending); /* prev does not change */ } } if (nrels > 0) { smgrdounlinkall(srels, nrels, false); for (i = 0; i < nrels; i++) smgrclose(srels[i]); } pfree(srels); }
int smgrGetPendingDeletes | ( | bool | forCommit, | |
RelFileNode ** | ptr | |||
) |
Definition at line 388 of file storage.c.
References PendingRelDelete::atCommit, PendingRelDelete::backend, GetCurrentTransactionNestLevel(), InvalidBackendId, PendingRelDelete::nestLevel, PendingRelDelete::next, palloc(), and PendingRelDelete::relnode.
Referenced by RecordTransactionAbort(), RecordTransactionCommit(), and StartPrepare().
{ int nestLevel = GetCurrentTransactionNestLevel(); int nrels; RelFileNode *rptr; PendingRelDelete *pending; nrels = 0; for (pending = pendingDeletes; pending != NULL; pending = pending->next) { if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit && pending->backend == InvalidBackendId) nrels++; } if (nrels == 0) { *ptr = NULL; return 0; } rptr = (RelFileNode *) palloc(nrels * sizeof(RelFileNode)); *ptr = rptr; for (pending = pendingDeletes; pending != NULL; pending = pending->next) { if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit && pending->backend == InvalidBackendId) { *rptr = pending->relnode; rptr++; } } return nrels; }
PendingRelDelete* pendingDeletes = NULL [static] |