Update obsolete comment in index_drop(). When the comment was written,
[PostgreSQL.git] / src / backend / catalog / storage.c
blob309fa469adf7fcfdbee9c904b930435e25f313a0
1 /*-------------------------------------------------------------------------
3 * storage.c
4 * code to create and destroy physical storage for relations
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
13 * NOTES
14 * Some of this code used to be in storage/smgr/smgr.c, and the
15 * function names still reflect that.
17 *-------------------------------------------------------------------------
20 #include "postgres.h"
22 #include "access/visibilitymap.h"
23 #include "access/xact.h"
24 #include "access/xlogutils.h"
25 #include "catalog/catalog.h"
26 #include "catalog/storage.h"
27 #include "storage/freespace.h"
28 #include "storage/smgr.h"
29 #include "utils/memutils.h"
30 #include "utils/rel.h"
33 * We keep a list of all relations (represented as RelFileNode values)
34 * that have been created or deleted in the current transaction. When
35 * a relation is created, we create the physical file immediately, but
36 * remember it so that we can delete the file again if the current
37 * transaction is aborted. Conversely, a deletion request is NOT
38 * executed immediately, but is just entered in the list. When and if
39 * the transaction commits, we can delete the physical file.
41 * To handle subtransactions, every entry is marked with its transaction
42 * nesting level. At subtransaction commit, we reassign the subtransaction's
43 * entries to the parent nesting level. At subtransaction abort, we can
44 * immediately execute the abort-time actions for all entries of the current
45 * nesting level.
47 * NOTE: the list is kept in TopMemoryContext to be sure it won't disappear
48 * unbetimes. It'd probably be OK to keep it in TopTransactionContext,
49 * but I'm being paranoid.
52 typedef struct PendingRelDelete
54 RelFileNode relnode; /* relation that may need to be deleted */
55 bool isTemp; /* is it a temporary relation? */
56 bool atCommit; /* T=delete at commit; F=delete at abort */
57 int nestLevel; /* xact nesting level of request */
58 struct PendingRelDelete *next; /* linked-list link */
59 } PendingRelDelete;
61 static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
64 * Declarations for smgr-related XLOG records
66 * Note: we log file creation and truncation here, but logging of deletion
67 * actions is handled by xact.c, because it is part of transaction commit.
70 /* XLOG gives us high 4 bits */
71 #define XLOG_SMGR_CREATE 0x10
72 #define XLOG_SMGR_TRUNCATE 0x20
74 typedef struct xl_smgr_create
76 RelFileNode rnode;
77 } xl_smgr_create;
79 typedef struct xl_smgr_truncate
81 BlockNumber blkno;
82 RelFileNode rnode;
83 } xl_smgr_truncate;
87 * RelationCreateStorage
88 * Create physical storage for a relation.
90 * Create the underlying disk file storage for the relation. This only
91 * creates the main fork; additional forks are created lazily by the
92 * modules that need them.
94 * This function is transactional. The creation is WAL-logged, and if the
95 * transaction aborts later on, the storage will be destroyed.
97 void
98 RelationCreateStorage(RelFileNode rnode, bool istemp)
100 PendingRelDelete *pending;
101 XLogRecPtr lsn;
102 XLogRecData rdata;
103 xl_smgr_create xlrec;
104 SMgrRelation srel;
106 srel = smgropen(rnode);
107 smgrcreate(srel, MAIN_FORKNUM, false);
109 if (!istemp)
112 * Make an XLOG entry showing the file creation. If we abort, the file
113 * will be dropped at abort time.
115 xlrec.rnode = rnode;
117 rdata.data = (char *) &xlrec;
118 rdata.len = sizeof(xlrec);
119 rdata.buffer = InvalidBuffer;
120 rdata.next = NULL;
122 lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE, &rdata);
125 /* Add the relation to the list of stuff to delete at abort */
126 pending = (PendingRelDelete *)
127 MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
128 pending->relnode = rnode;
129 pending->isTemp = istemp;
130 pending->atCommit = false; /* delete if abort */
131 pending->nestLevel = GetCurrentTransactionNestLevel();
132 pending->next = pendingDeletes;
133 pendingDeletes = pending;
137 * RelationDropStorage
138 * Schedule unlinking of physical storage at transaction commit.
140 void
141 RelationDropStorage(Relation rel)
143 PendingRelDelete *pending;
145 /* Add the relation to the list of stuff to delete at commit */
146 pending = (PendingRelDelete *)
147 MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
148 pending->relnode = rel->rd_node;
149 pending->isTemp = rel->rd_istemp;
150 pending->atCommit = true; /* delete if commit */
151 pending->nestLevel = GetCurrentTransactionNestLevel();
152 pending->next = pendingDeletes;
153 pendingDeletes = pending;
156 * NOTE: if the relation was created in this transaction, it will now be
157 * present in the pending-delete list twice, once with atCommit true and
158 * once with atCommit false. Hence, it will be physically deleted at end
159 * of xact in either case (and the other entry will be ignored by
160 * smgrDoPendingDeletes, so no error will occur). We could instead remove
161 * the existing list entry and delete the physical file immediately, but
162 * for now I'll keep the logic simple.
165 RelationCloseSmgr(rel);
169 * RelationTruncate
170 * Physically truncate a relation to the specified number of blocks.
172 * This includes getting rid of any buffers for the blocks that are to be
173 * dropped. If 'fsm' is true, the FSM of the relation is truncated as well.
175 void
176 RelationTruncate(Relation rel, BlockNumber nblocks)
178 bool fsm;
179 bool vm;
181 /* Open it at the smgr level if not already done */
182 RelationOpenSmgr(rel);
184 /* Make sure rd_targblock isn't pointing somewhere past end */
185 rel->rd_targblock = InvalidBlockNumber;
187 /* Truncate the FSM first if it exists */
188 fsm = smgrexists(rel->rd_smgr, FSM_FORKNUM);
189 if (fsm)
190 FreeSpaceMapTruncateRel(rel, nblocks);
192 /* Truncate the visibility map too if it exists. */
193 vm = smgrexists(rel->rd_smgr, VISIBILITYMAP_FORKNUM);
194 if (vm)
195 visibilitymap_truncate(rel, nblocks);
198 * We WAL-log the truncation before actually truncating, which
199 * means trouble if the truncation fails. If we then crash, the WAL
200 * replay likely isn't going to succeed in the truncation either, and
201 * cause a PANIC. It's tempting to put a critical section here, but
202 * that cure would be worse than the disease. It would turn a usually
203 * harmless failure to truncate, that could spell trouble at WAL replay,
204 * into a certain PANIC.
206 if (!rel->rd_istemp)
209 * Make an XLOG entry showing the file truncation.
211 XLogRecPtr lsn;
212 XLogRecData rdata;
213 xl_smgr_truncate xlrec;
215 xlrec.blkno = nblocks;
216 xlrec.rnode = rel->rd_node;
218 rdata.data = (char *) &xlrec;
219 rdata.len = sizeof(xlrec);
220 rdata.buffer = InvalidBuffer;
221 rdata.next = NULL;
223 lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_TRUNCATE, &rdata);
226 * Flush, because otherwise the truncation of the main relation
227 * might hit the disk before the WAL record, and the truncation of
228 * the FSM or visibility map. If we crashed during that window, we'd
229 * be left with a truncated heap, but the FSM or visibility map would
230 * still contain entries for the non-existent heap pages.
232 if (fsm || vm)
233 XLogFlush(lsn);
236 /* Do the real work */
237 smgrtruncate(rel->rd_smgr, MAIN_FORKNUM, nblocks, rel->rd_istemp);
241 * smgrDoPendingDeletes() -- Take care of relation deletes at end of xact.
243 * This also runs when aborting a subxact; we want to clean up a failed
244 * subxact immediately.
246 void
247 smgrDoPendingDeletes(bool isCommit)
249 int nestLevel = GetCurrentTransactionNestLevel();
250 PendingRelDelete *pending;
251 PendingRelDelete *prev;
252 PendingRelDelete *next;
254 prev = NULL;
255 for (pending = pendingDeletes; pending != NULL; pending = next)
257 next = pending->next;
258 if (pending->nestLevel < nestLevel)
260 /* outer-level entries should not be processed yet */
261 prev = pending;
263 else
265 /* unlink list entry first, so we don't retry on failure */
266 if (prev)
267 prev->next = next;
268 else
269 pendingDeletes = next;
270 /* do deletion if called for */
271 if (pending->atCommit == isCommit)
273 int i;
275 /* schedule unlinking old files */
276 SMgrRelation srel;
278 srel = smgropen(pending->relnode);
279 for (i = 0; i <= MAX_FORKNUM; i++)
281 if (smgrexists(srel, i))
282 smgrdounlink(srel,
284 pending->isTemp,
285 false);
287 smgrclose(srel);
289 /* must explicitly free the list entry */
290 pfree(pending);
291 /* prev does not change */
297 * smgrGetPendingDeletes() -- Get a list of relations to be deleted.
299 * The return value is the number of relations scheduled for termination.
300 * *ptr is set to point to a freshly-palloc'd array of RelFileNodes.
301 * If there are no relations to be deleted, *ptr is set to NULL.
303 * If haveNonTemp isn't NULL, the bool it points to gets set to true if
304 * there is any non-temp table pending to be deleted; false if not.
306 * Note that the list does not include anything scheduled for termination
307 * by upper-level transactions.
310 smgrGetPendingDeletes(bool forCommit, RelFileNode **ptr, bool *haveNonTemp)
312 int nestLevel = GetCurrentTransactionNestLevel();
313 int nrels;
314 RelFileNode *rptr;
315 PendingRelDelete *pending;
317 nrels = 0;
318 if (haveNonTemp)
319 *haveNonTemp = false;
320 for (pending = pendingDeletes; pending != NULL; pending = pending->next)
322 if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit)
323 nrels++;
325 if (nrels == 0)
327 *ptr = NULL;
328 return 0;
330 rptr = (RelFileNode *) palloc(nrels * sizeof(RelFileNode));
331 *ptr = rptr;
332 for (pending = pendingDeletes; pending != NULL; pending = pending->next)
334 if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit)
336 *rptr = pending->relnode;
337 rptr++;
339 if (haveNonTemp && !pending->isTemp)
340 *haveNonTemp = true;
342 return nrels;
346 * PostPrepare_smgr -- Clean up after a successful PREPARE
348 * What we have to do here is throw away the in-memory state about pending
349 * relation deletes. It's all been recorded in the 2PC state file and
350 * it's no longer smgr's job to worry about it.
352 void
353 PostPrepare_smgr(void)
355 PendingRelDelete *pending;
356 PendingRelDelete *next;
358 for (pending = pendingDeletes; pending != NULL; pending = next)
360 next = pending->next;
361 pendingDeletes = next;
362 /* must explicitly free the list entry */
363 pfree(pending);
369 * AtSubCommit_smgr() --- Take care of subtransaction commit.
371 * Reassign all items in the pending-deletes list to the parent transaction.
373 void
374 AtSubCommit_smgr(void)
376 int nestLevel = GetCurrentTransactionNestLevel();
377 PendingRelDelete *pending;
379 for (pending = pendingDeletes; pending != NULL; pending = pending->next)
381 if (pending->nestLevel >= nestLevel)
382 pending->nestLevel = nestLevel - 1;
387 * AtSubAbort_smgr() --- Take care of subtransaction abort.
389 * Delete created relations and forget about deleted relations.
390 * We can execute these operations immediately because we know this
391 * subtransaction will not commit.
393 void
394 AtSubAbort_smgr(void)
396 smgrDoPendingDeletes(false);
399 void
400 smgr_redo(XLogRecPtr lsn, XLogRecord *record)
402 uint8 info = record->xl_info & ~XLR_INFO_MASK;
404 /* Backup blocks are not used in smgr records */
405 Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
407 if (info == XLOG_SMGR_CREATE)
409 xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
410 SMgrRelation reln;
412 reln = smgropen(xlrec->rnode);
413 smgrcreate(reln, MAIN_FORKNUM, true);
415 else if (info == XLOG_SMGR_TRUNCATE)
417 xl_smgr_truncate *xlrec = (xl_smgr_truncate *) XLogRecGetData(record);
418 SMgrRelation reln;
420 reln = smgropen(xlrec->rnode);
423 * Forcibly create relation if it doesn't exist (which suggests that
424 * it was dropped somewhere later in the WAL sequence). As in
425 * XLogOpenRelation, we prefer to recreate the rel and replay the log
426 * as best we can until the drop is seen.
428 smgrcreate(reln, MAIN_FORKNUM, true);
430 smgrtruncate(reln, MAIN_FORKNUM, xlrec->blkno, false);
432 /* Also tell xlogutils.c about it */
433 XLogTruncateRelation(xlrec->rnode, MAIN_FORKNUM, xlrec->blkno);
435 /* Truncate FSM too */
436 if (smgrexists(reln, FSM_FORKNUM))
438 Relation rel = CreateFakeRelcacheEntry(xlrec->rnode);
439 FreeSpaceMapTruncateRel(rel, xlrec->blkno);
440 FreeFakeRelcacheEntry(rel);
444 else
445 elog(PANIC, "smgr_redo: unknown op code %u", info);
448 void
449 smgr_desc(StringInfo buf, uint8 xl_info, char *rec)
451 uint8 info = xl_info & ~XLR_INFO_MASK;
453 if (info == XLOG_SMGR_CREATE)
455 xl_smgr_create *xlrec = (xl_smgr_create *) rec;
456 char *path = relpath(xlrec->rnode, MAIN_FORKNUM);
458 appendStringInfo(buf, "file create: %s", path);
459 pfree(path);
461 else if (info == XLOG_SMGR_TRUNCATE)
463 xl_smgr_truncate *xlrec = (xl_smgr_truncate *) rec;
464 char *path = relpath(xlrec->rnode, MAIN_FORKNUM);
466 appendStringInfo(buf, "file truncate: %s to %u blocks", path,
467 xlrec->blkno);
468 pfree(path);
470 else
471 appendStringInfo(buf, "UNKNOWN");