]> granicus.if.org Git - postgresql/blob - src/backend/catalog/storage.c
Add a new option to RestoreBkpBlocks() to indicate if a cleanup lock should
[postgresql] / src / backend / catalog / storage.c
1 /*-------------------------------------------------------------------------
2  *
3  * storage.c
4  *        code to create and destroy physical storage for relations
5  *
6  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/catalog/storage.c,v 1.5 2009/01/20 18:59:37 heikki Exp $
12  *
13  * NOTES
14  *        Some of this code used to be in storage/smgr/smgr.c, and the
15  *        function names still reflect that.
16  *
17  *-------------------------------------------------------------------------
18  */
19
20 #include "postgres.h"
21
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"
31
32 /*
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.
40  *
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.
46  *
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.
50  */
51
52 typedef struct PendingRelDelete
53 {
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;
60
61 static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
62
63 /*
64  * Declarations for smgr-related XLOG records
65  *
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.
68  */
69
70 /* XLOG gives us high 4 bits */
71 #define XLOG_SMGR_CREATE        0x10
72 #define XLOG_SMGR_TRUNCATE      0x20
73
74 typedef struct xl_smgr_create
75 {
76         RelFileNode rnode;
77 } xl_smgr_create;
78
79 typedef struct xl_smgr_truncate
80 {
81         BlockNumber blkno;
82         RelFileNode rnode;
83 } xl_smgr_truncate;
84
85
86 /*
87  * RelationCreateStorage
88  *              Create physical storage for a relation.
89  *
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.
93  *
94  * This function is transactional. The creation is WAL-logged, and if the
95  * transaction aborts later on, the storage will be destroyed.
96  */
97 void
98 RelationCreateStorage(RelFileNode rnode, bool istemp)
99 {
100         PendingRelDelete *pending;
101         XLogRecPtr      lsn;
102         XLogRecData rdata;
103         xl_smgr_create xlrec;
104         SMgrRelation srel;
105
106         srel = smgropen(rnode);
107         smgrcreate(srel, MAIN_FORKNUM, false);
108
109         if (!istemp)
110         {
111                 /*
112                  * Make an XLOG entry showing the file creation.  If we abort, the file
113                  * will be dropped at abort time.
114                  */
115                 xlrec.rnode = rnode;
116
117                 rdata.data = (char *) &xlrec;
118                 rdata.len = sizeof(xlrec);
119                 rdata.buffer = InvalidBuffer;
120                 rdata.next = NULL;
121
122                 lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE, &rdata);
123         }
124
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;
134 }
135
136 /*
137  * RelationDropStorage
138  *              Schedule unlinking of physical storage at transaction commit.
139  */
140 void
141 RelationDropStorage(Relation rel)
142 {
143         PendingRelDelete *pending;
144
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;
154
155         /*
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.
163          */
164
165         RelationCloseSmgr(rel);
166 }
167
168 /*
169  * RelationTruncate
170  *              Physically truncate a relation to the specified number of blocks.
171  *
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.
174  */
175 void
176 RelationTruncate(Relation rel, BlockNumber nblocks)
177 {
178         bool fsm;
179         bool vm;
180
181         /* Open it at the smgr level if not already done */
182         RelationOpenSmgr(rel);
183
184         /* Make sure rd_targblock isn't pointing somewhere past end */
185         rel->rd_targblock = InvalidBlockNumber;
186
187         /* Truncate the FSM first if it exists */
188         fsm = smgrexists(rel->rd_smgr, FSM_FORKNUM);
189         if (fsm)
190                 FreeSpaceMapTruncateRel(rel, nblocks);
191
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);
196
197         /*
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.
205          */
206         if (!rel->rd_istemp)
207         {
208                 /*
209                  * Make an XLOG entry showing the file truncation.
210                  */
211                 XLogRecPtr      lsn;
212                 XLogRecData rdata;
213                 xl_smgr_truncate xlrec;
214
215                 xlrec.blkno = nblocks;
216                 xlrec.rnode = rel->rd_node;
217
218                 rdata.data = (char *) &xlrec;
219                 rdata.len = sizeof(xlrec);
220                 rdata.buffer = InvalidBuffer;
221                 rdata.next = NULL;
222
223                 lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_TRUNCATE, &rdata);
224
225                 /*
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.
231                  */
232                 if (fsm || vm)
233                         XLogFlush(lsn);
234         }
235
236         /* Do the real work */
237         smgrtruncate(rel->rd_smgr, MAIN_FORKNUM, nblocks, rel->rd_istemp);
238 }
239
240 /*
241  *      smgrDoPendingDeletes() -- Take care of relation deletes at end of xact.
242  *
243  * This also runs when aborting a subxact; we want to clean up a failed
244  * subxact immediately.
245  */
246 void
247 smgrDoPendingDeletes(bool isCommit)
248 {
249         int                     nestLevel = GetCurrentTransactionNestLevel();
250         PendingRelDelete *pending;
251         PendingRelDelete *prev;
252         PendingRelDelete *next;
253
254         prev = NULL;
255         for (pending = pendingDeletes; pending != NULL; pending = next)
256         {
257                 next = pending->next;
258                 if (pending->nestLevel < nestLevel)
259                 {
260                         /* outer-level entries should not be processed yet */
261                         prev = pending;
262                 }
263                 else
264                 {
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)
272                         {
273                                 int i;
274
275                                 /* schedule unlinking old files */
276                                 SMgrRelation srel;
277
278                                 srel = smgropen(pending->relnode);
279                                 for (i = 0; i <= MAX_FORKNUM; i++)
280                                 {
281                                         if (smgrexists(srel, i))
282                                                 smgrdounlink(srel,
283                                                                          i,
284                                                                          pending->isTemp,
285                                                                          false);
286                                 }
287                                 smgrclose(srel);
288                         }
289                         /* must explicitly free the list entry */
290                         pfree(pending);
291                         /* prev does not change */
292                 }
293         }
294 }
295
296 /*
297  * smgrGetPendingDeletes() -- Get a list of relations to be deleted.
298  *
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.
302  *
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.
305  *
306  * Note that the list does not include anything scheduled for termination
307  * by upper-level transactions.
308  */
309 int
310 smgrGetPendingDeletes(bool forCommit, RelFileNode **ptr, bool *haveNonTemp)
311 {
312         int                     nestLevel = GetCurrentTransactionNestLevel();
313         int                     nrels;
314         RelFileNode *rptr;
315         PendingRelDelete *pending;
316
317         nrels = 0;
318         if (haveNonTemp)
319                 *haveNonTemp = false;
320         for (pending = pendingDeletes; pending != NULL; pending = pending->next)
321         {
322                 if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit)
323                         nrels++;
324         }
325         if (nrels == 0)
326         {
327                 *ptr = NULL;
328                 return 0;
329         }
330         rptr = (RelFileNode *) palloc(nrels * sizeof(RelFileNode));
331         *ptr = rptr;
332         for (pending = pendingDeletes; pending != NULL; pending = pending->next)
333         {
334                 if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit)
335                 {
336                         *rptr = pending->relnode;
337                         rptr++;
338                 }
339                 if (haveNonTemp && !pending->isTemp)
340                         *haveNonTemp = true;
341         }
342         return nrels;
343 }
344
345 /*
346  *      PostPrepare_smgr -- Clean up after a successful PREPARE
347  *
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.
351  */
352 void
353 PostPrepare_smgr(void)
354 {
355         PendingRelDelete *pending;
356         PendingRelDelete *next;
357
358         for (pending = pendingDeletes; pending != NULL; pending = next)
359         {
360                 next = pending->next;
361                 pendingDeletes = next;
362                 /* must explicitly free the list entry */
363                 pfree(pending);
364         }
365 }
366
367
368 /*
369  * AtSubCommit_smgr() --- Take care of subtransaction commit.
370  *
371  * Reassign all items in the pending-deletes list to the parent transaction.
372  */
373 void
374 AtSubCommit_smgr(void)
375 {
376         int                     nestLevel = GetCurrentTransactionNestLevel();
377         PendingRelDelete *pending;
378
379         for (pending = pendingDeletes; pending != NULL; pending = pending->next)
380         {
381                 if (pending->nestLevel >= nestLevel)
382                         pending->nestLevel = nestLevel - 1;
383         }
384 }
385
386 /*
387  * AtSubAbort_smgr() --- Take care of subtransaction abort.
388  *
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.
392  */
393 void
394 AtSubAbort_smgr(void)
395 {
396         smgrDoPendingDeletes(false);
397 }
398
399 void
400 smgr_redo(XLogRecPtr lsn, XLogRecord *record)
401 {
402         uint8           info = record->xl_info & ~XLR_INFO_MASK;
403
404         /* Backup blocks are not used in smgr records */
405         Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
406
407         if (info == XLOG_SMGR_CREATE)
408         {
409                 xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
410                 SMgrRelation reln;
411
412                 reln = smgropen(xlrec->rnode);
413                 smgrcreate(reln, MAIN_FORKNUM, true);
414         }
415         else if (info == XLOG_SMGR_TRUNCATE)
416         {
417                 xl_smgr_truncate *xlrec = (xl_smgr_truncate *) XLogRecGetData(record);
418                 SMgrRelation reln;
419
420                 reln = smgropen(xlrec->rnode);
421
422                 /*
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.
427                  */
428                 smgrcreate(reln, MAIN_FORKNUM, true);
429
430                 smgrtruncate(reln, MAIN_FORKNUM, xlrec->blkno, false);
431
432                 /* Also tell xlogutils.c about it */
433                 XLogTruncateRelation(xlrec->rnode, MAIN_FORKNUM, xlrec->blkno);
434  
435                 /* Truncate FSM too */
436                 if (smgrexists(reln, FSM_FORKNUM))
437                 {
438                         Relation rel = CreateFakeRelcacheEntry(xlrec->rnode);
439                         FreeSpaceMapTruncateRel(rel, xlrec->blkno);
440                         FreeFakeRelcacheEntry(rel);
441                 }
442
443         }
444         else
445                 elog(PANIC, "smgr_redo: unknown op code %u", info);
446 }
447
448 void
449 smgr_desc(StringInfo buf, uint8 xl_info, char *rec)
450 {
451         uint8           info = xl_info & ~XLR_INFO_MASK;
452
453         if (info == XLOG_SMGR_CREATE)
454         {
455                 xl_smgr_create *xlrec = (xl_smgr_create *) rec;
456                 char *path = relpath(xlrec->rnode, MAIN_FORKNUM);
457
458                 appendStringInfo(buf, "file create: %s", path);
459                 pfree(path);
460         }
461         else if (info == XLOG_SMGR_TRUNCATE)
462         {
463                 xl_smgr_truncate *xlrec = (xl_smgr_truncate *) rec;
464                 char *path = relpath(xlrec->rnode, MAIN_FORKNUM);
465
466                 appendStringInfo(buf, "file truncate: %s to %u blocks", path,
467                                                  xlrec->blkno);
468                 pfree(path);
469         }
470         else
471                 appendStringInfo(buf, "UNKNOWN");
472 }