Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * storage.c
4 : : * code to create and destroy physical storage for relations
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/catalog/storage.c
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/xlog.h"
25 : : #include "access/xloginsert.h"
26 : : #include "access/xlogutils.h"
27 : : #include "catalog/storage.h"
28 : : #include "catalog/storage_xlog.h"
29 : : #include "miscadmin.h"
30 : : #include "storage/bulk_write.h"
31 : : #include "storage/freespace.h"
32 : : #include "storage/proc.h"
33 : : #include "storage/smgr.h"
34 : : #include "utils/hsearch.h"
35 : : #include "utils/memutils.h"
36 : : #include "utils/rel.h"
37 : :
38 : : /* GUC variables */
39 : : int wal_skip_threshold = 2048; /* in kilobytes */
40 : :
41 : : /*
42 : : * We keep a list of all relations (represented as RelFileLocator values)
43 : : * that have been created or deleted in the current transaction. When
44 : : * a relation is created, we create the physical file immediately, but
45 : : * remember it so that we can delete the file again if the current
46 : : * transaction is aborted. Conversely, a deletion request is NOT
47 : : * executed immediately, but is just entered in the list. When and if
48 : : * the transaction commits, we can delete the physical file.
49 : : *
50 : : * To handle subtransactions, every entry is marked with its transaction
51 : : * nesting level. At subtransaction commit, we reassign the subtransaction's
52 : : * entries to the parent nesting level. At subtransaction abort, we can
53 : : * immediately execute the abort-time actions for all entries of the current
54 : : * nesting level.
55 : : *
56 : : * NOTE: the list is kept in TopMemoryContext to be sure it won't disappear
57 : : * unbetimes. It'd probably be OK to keep it in TopTransactionContext,
58 : : * but I'm being paranoid.
59 : : */
60 : :
61 : : typedef struct PendingRelDelete
62 : : {
63 : : RelFileLocator rlocator; /* relation that may need to be deleted */
64 : : ProcNumber procNumber; /* INVALID_PROC_NUMBER if not a temp rel */
65 : : bool atCommit; /* T=delete at commit; F=delete at abort */
66 : : int nestLevel; /* xact nesting level of request */
67 : : struct PendingRelDelete *next; /* linked-list link */
68 : : } PendingRelDelete;
69 : :
70 : : typedef struct PendingRelSync
71 : : {
72 : : RelFileLocator rlocator;
73 : : bool is_truncated; /* Has the file experienced truncation? */
74 : : } PendingRelSync;
75 : :
76 : : static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
77 : : static HTAB *pendingSyncHash = NULL;
78 : :
79 : :
80 : : /*
81 : : * AddPendingSync
82 : : * Queue an at-commit fsync.
83 : : */
84 : : static void
648 rhaas@postgresql.org 85 :CBC 35486 : AddPendingSync(const RelFileLocator *rlocator)
86 : : {
87 : : PendingRelSync *pending;
88 : : bool found;
89 : :
90 : : /* create the hash if not yet */
1471 noah@leadboat.com 91 [ + + ]: 35486 : if (!pendingSyncHash)
92 : : {
93 : : HASHCTL ctl;
94 : :
648 rhaas@postgresql.org 95 : 5869 : ctl.keysize = sizeof(RelFileLocator);
1471 noah@leadboat.com 96 : 5869 : ctl.entrysize = sizeof(PendingRelSync);
97 : 5869 : ctl.hcxt = TopTransactionContext;
98 : 5869 : pendingSyncHash = hash_create("pending sync hash", 16, &ctl,
99 : : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
100 : : }
101 : :
648 rhaas@postgresql.org 102 : 35486 : pending = hash_search(pendingSyncHash, rlocator, HASH_ENTER, &found);
1471 noah@leadboat.com 103 [ - + ]: 35486 : Assert(!found);
104 : 35486 : pending->is_truncated = false;
105 : 35486 : }
106 : :
107 : : /*
108 : : * RelationCreateStorage
109 : : * Create physical storage for a relation.
110 : : *
111 : : * Create the underlying disk file storage for the relation. This only
112 : : * creates the main fork; additional forks are created lazily by the
113 : : * modules that need them.
114 : : *
115 : : * This function is transactional. The creation is WAL-logged, and if the
116 : : * transaction aborts later on, the storage will be destroyed. A caller
117 : : * that does not want the storage to be destroyed in case of an abort may
118 : : * pass register_delete = false.
119 : : */
120 : : SMgrRelation
648 rhaas@postgresql.org 121 : 102073 : RelationCreateStorage(RelFileLocator rlocator, char relpersistence,
122 : : bool register_delete)
123 : : {
124 : : SMgrRelation srel;
125 : : ProcNumber procNumber;
126 : : bool needs_wal;
127 : :
1471 noah@leadboat.com 128 [ - + ]: 102073 : Assert(!IsInParallelMode()); /* couldn't update pendingSyncHash */
129 : :
4871 rhaas@postgresql.org 130 [ + + + - ]: 102073 : switch (relpersistence)
131 : : {
132 : 2913 : case RELPERSISTENCE_TEMP:
42 heikki.linnakangas@i 133 [ + - ]:GNC 2913 : procNumber = ProcNumberForTempRelations();
4871 rhaas@postgresql.org 134 :CBC 2913 : needs_wal = false;
135 : 2913 : break;
4855 136 : 283 : case RELPERSISTENCE_UNLOGGED:
42 heikki.linnakangas@i 137 :GNC 283 : procNumber = INVALID_PROC_NUMBER;
4855 rhaas@postgresql.org 138 :CBC 283 : needs_wal = false;
139 : 283 : break;
4871 140 : 98877 : case RELPERSISTENCE_PERMANENT:
42 heikki.linnakangas@i 141 :GNC 98877 : procNumber = INVALID_PROC_NUMBER;
4871 rhaas@postgresql.org 142 :CBC 98877 : needs_wal = true;
143 : 98877 : break;
4871 rhaas@postgresql.org 144 :UBC 0 : default:
145 [ # # ]: 0 : elog(ERROR, "invalid relpersistence: %c", relpersistence);
146 : : return NULL; /* placate compiler */
147 : : }
148 : :
42 heikki.linnakangas@i 149 :GNC 102073 : srel = smgropen(rlocator, procNumber);
5625 heikki.linnakangas@i 150 :CBC 102073 : smgrcreate(srel, MAIN_FORKNUM, false);
151 : :
4871 rhaas@postgresql.org 152 [ + + ]: 102073 : if (needs_wal)
648 153 : 98877 : log_smgrcreate(&srel->smgr_rlocator.locator, MAIN_FORKNUM);
154 : :
155 : : /*
156 : : * Add the relation to the list of stuff to delete at abort, if we are
157 : : * asked to do so.
158 : : */
747 159 [ + + ]: 102073 : if (register_delete)
160 : : {
161 : : PendingRelDelete *pending;
162 : :
163 : : pending = (PendingRelDelete *)
164 : 54106 : MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
648 165 : 54106 : pending->rlocator = rlocator;
42 heikki.linnakangas@i 166 :GNC 54106 : pending->procNumber = procNumber;
747 rhaas@postgresql.org 167 :CBC 54106 : pending->atCommit = false; /* delete if abort */
168 : 54106 : pending->nestLevel = GetCurrentTransactionNestLevel();
169 : 54106 : pending->next = pendingDeletes;
170 : 54106 : pendingDeletes = pending;
171 : : }
172 : :
1471 noah@leadboat.com 173 [ + + + + ]: 102073 : if (relpersistence == RELPERSISTENCE_PERMANENT && !XLogIsNeeded())
174 : : {
42 heikki.linnakangas@i 175 [ - + ]:GNC 33694 : Assert(procNumber == INVALID_PROC_NUMBER);
648 rhaas@postgresql.org 176 :CBC 33694 : AddPendingSync(&rlocator);
177 : : }
178 : :
1812 andres@anarazel.de 179 : 102073 : return srel;
180 : : }
181 : :
182 : : /*
183 : : * Perform XLogInsert of an XLOG_SMGR_CREATE record to WAL.
184 : : */
185 : : void
648 rhaas@postgresql.org 186 : 115149 : log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
187 : : {
188 : : xl_smgr_create xlrec;
189 : :
190 : : /*
191 : : * Make an XLOG entry reporting the file creation.
192 : : */
193 : 115149 : xlrec.rlocator = *rlocator;
4855 194 : 115149 : xlrec.forkNum = forkNum;
195 : :
3433 heikki.linnakangas@i 196 : 115149 : XLogBeginInsert();
197 : 115149 : XLogRegisterData((char *) &xlrec, sizeof(xlrec));
198 : 115149 : XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE | XLR_SPECIAL_REL_UPDATE);
4855 rhaas@postgresql.org 199 : 115149 : }
200 : :
201 : : /*
202 : : * RelationDropStorage
203 : : * Schedule unlinking of physical storage at transaction commit.
204 : : */
205 : : void
5625 heikki.linnakangas@i 206 : 34210 : RelationDropStorage(Relation rel)
207 : : {
208 : : PendingRelDelete *pending;
209 : :
210 : : /* Add the relation to the list of stuff to delete at commit */
211 : : pending = (PendingRelDelete *)
212 : 34210 : MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
648 rhaas@postgresql.org 213 : 34210 : pending->rlocator = rel->rd_locator;
42 heikki.linnakangas@i 214 :GNC 34210 : pending->procNumber = rel->rd_backend;
5625 heikki.linnakangas@i 215 :CBC 34210 : pending->atCommit = true; /* delete if commit */
216 : 34210 : pending->nestLevel = GetCurrentTransactionNestLevel();
217 : 34210 : pending->next = pendingDeletes;
218 : 34210 : pendingDeletes = pending;
219 : :
220 : : /*
221 : : * NOTE: if the relation was created in this transaction, it will now be
222 : : * present in the pending-delete list twice, once with atCommit true and
223 : : * once with atCommit false. Hence, it will be physically deleted at end
224 : : * of xact in either case (and the other entry will be ignored by
225 : : * smgrDoPendingDeletes, so no error will occur). We could instead remove
226 : : * the existing list entry and delete the physical file immediately, but
227 : : * for now I'll keep the logic simple.
228 : : */
229 : :
230 : 34210 : RelationCloseSmgr(rel);
231 : 34210 : }
232 : :
233 : : /*
234 : : * RelationPreserveStorage
235 : : * Mark a relation as not to be deleted after all.
236 : : *
237 : : * We need this function because relation mapping changes are committed
238 : : * separately from commit of the whole transaction, so it's still possible
239 : : * for the transaction to abort after the mapping update is done.
240 : : * When a new physical relation is installed in the map, it would be
241 : : * scheduled for delete-on-abort, so we'd delete it, and be in trouble.
242 : : * The relation mapper fixes this by telling us to not delete such relations
243 : : * after all as part of its commit.
244 : : *
245 : : * We also use this to reuse an old build of an index during ALTER TABLE, this
246 : : * time removing the delete-at-commit entry.
247 : : *
248 : : * No-op if the relation is not among those scheduled for deletion.
249 : : */
250 : : void
648 rhaas@postgresql.org 251 : 6236 : RelationPreserveStorage(RelFileLocator rlocator, bool atCommit)
252 : : {
253 : : PendingRelDelete *pending;
254 : : PendingRelDelete *prev;
255 : : PendingRelDelete *next;
256 : :
5180 tgl@sss.pgh.pa.us 257 : 6236 : prev = NULL;
258 [ + + ]: 37560 : for (pending = pendingDeletes; pending != NULL; pending = next)
259 : : {
260 : 31324 : next = pending->next;
648 rhaas@postgresql.org 261 [ + + + - : 31324 : if (RelFileLocatorEquals(rlocator, pending->rlocator)
+ - ]
4654 262 [ + + ]: 523 : && pending->atCommit == atCommit)
263 : : {
264 : : /* unlink and delete list entry */
5180 tgl@sss.pgh.pa.us 265 [ + + ]: 520 : if (prev)
266 : 378 : prev->next = next;
267 : : else
268 : 142 : pendingDeletes = next;
269 : 520 : pfree(pending);
270 : : /* prev does not change */
271 : : }
272 : : else
273 : : {
274 : : /* unrelated entry, don't touch it */
275 : 30804 : prev = pending;
276 : : }
277 : : }
278 : 6236 : }
279 : :
280 : : /*
281 : : * RelationTruncate
282 : : * Physically truncate a relation to the specified number of blocks.
283 : : *
284 : : * This includes getting rid of any buffers for the blocks that are to be
285 : : * dropped.
286 : : */
287 : : void
5625 heikki.linnakangas@i 288 : 524 : RelationTruncate(Relation rel, BlockNumber nblocks)
289 : : {
290 : : bool fsm;
291 : : bool vm;
1664 fujii@postgresql.org 292 : 524 : bool need_fsm_vacuum = false;
293 : : ForkNumber forks[MAX_FORKNUM];
294 : : BlockNumber blocks[MAX_FORKNUM];
1431 tgl@sss.pgh.pa.us 295 : 524 : int nforks = 0;
296 : : SMgrRelation reln;
297 : :
298 : : /*
299 : : * Make sure smgr_targblock etc aren't pointing somewhere past new end.
300 : : * (Note: don't rely on this reln pointer below this loop.)
301 : : */
1007 302 : 524 : reln = RelationGetSmgr(rel);
303 : 524 : reln->smgr_targblock = InvalidBlockNumber;
1353 tmunro@postgresql.or 304 [ + + ]: 2620 : for (int i = 0; i <= MAX_FORKNUM; ++i)
1007 tgl@sss.pgh.pa.us 305 : 2096 : reln->smgr_cached_nblocks[i] = InvalidBlockNumber;
306 : :
307 : : /* Prepare for truncation of MAIN fork of the relation */
1664 fujii@postgresql.org 308 : 524 : forks[nforks] = MAIN_FORKNUM;
309 : 524 : blocks[nforks] = nblocks;
310 : 524 : nforks++;
311 : :
312 : : /* Prepare for truncation of the FSM if it exists */
1007 tgl@sss.pgh.pa.us 313 : 524 : fsm = smgrexists(RelationGetSmgr(rel), FSM_FORKNUM);
5625 heikki.linnakangas@i 314 [ + + ]: 524 : if (fsm)
315 : : {
1664 fujii@postgresql.org 316 : 165 : blocks[nforks] = FreeSpaceMapPrepareTruncateRel(rel, nblocks);
317 [ + - ]: 165 : if (BlockNumberIsValid(blocks[nforks]))
318 : : {
319 : 165 : forks[nforks] = FSM_FORKNUM;
320 : 165 : nforks++;
321 : 165 : need_fsm_vacuum = true;
322 : : }
323 : : }
324 : :
325 : : /* Prepare for truncation of the visibility map too if it exists */
1007 tgl@sss.pgh.pa.us 326 : 524 : vm = smgrexists(RelationGetSmgr(rel), VISIBILITYMAP_FORKNUM);
5611 heikki.linnakangas@i 327 [ + + ]: 524 : if (vm)
328 : : {
1664 fujii@postgresql.org 329 : 165 : blocks[nforks] = visibilitymap_prepare_truncate(rel, nblocks);
330 [ + + ]: 165 : if (BlockNumberIsValid(blocks[nforks]))
331 : : {
332 : 60 : forks[nforks] = VISIBILITYMAP_FORKNUM;
333 : 60 : nforks++;
334 : : }
335 : : }
336 : :
1471 noah@leadboat.com 337 : 524 : RelationPreTruncate(rel);
338 : :
339 : : /*
340 : : * Make sure that a concurrent checkpoint can't complete while truncation
341 : : * is in progress.
342 : : *
343 : : * The truncation operation might drop buffers that the checkpoint
344 : : * otherwise would have flushed. If it does, then it's essential that the
345 : : * files actually get truncated on disk before the checkpoint record is
346 : : * written. Otherwise, if reply begins from that checkpoint, the
347 : : * to-be-truncated blocks might still exist on disk but have older
348 : : * contents than expected, which can cause replay to fail. It's OK for the
349 : : * blocks to not exist on disk at all, but not for them to have the wrong
350 : : * contents.
351 : : */
737 rhaas@postgresql.org 352 [ - + ]: 524 : Assert((MyProc->delayChkptFlags & DELAY_CHKPT_COMPLETE) == 0);
353 : 524 : MyProc->delayChkptFlags |= DELAY_CHKPT_COMPLETE;
354 : :
355 : : /*
356 : : * We WAL-log the truncation before actually truncating, which means
357 : : * trouble if the truncation fails. If we then crash, the WAL replay
358 : : * likely isn't going to succeed in the truncation either, and cause a
359 : : * PANIC. It's tempting to put a critical section here, but that cure
360 : : * would be worse than the disease. It would turn a usually harmless
361 : : * failure to truncate, that might spell trouble at WAL replay, into a
362 : : * certain PANIC.
363 : : */
4871 364 [ + + + + : 524 : if (RelationNeedsWAL(rel))
+ + + - ]
365 : : {
366 : : /*
367 : : * Make an XLOG entry reporting the file truncation.
368 : : */
369 : : XLogRecPtr lsn;
370 : : xl_smgr_truncate xlrec;
371 : :
5625 heikki.linnakangas@i 372 : 192 : xlrec.blkno = nblocks;
648 rhaas@postgresql.org 373 : 192 : xlrec.rlocator = rel->rd_locator;
2858 374 : 192 : xlrec.flags = SMGR_TRUNCATE_ALL;
375 : :
3433 heikki.linnakangas@i 376 : 192 : XLogBeginInsert();
377 : 192 : XLogRegisterData((char *) &xlrec, sizeof(xlrec));
378 : :
379 : 192 : lsn = XLogInsert(RM_SMGR_ID,
380 : : XLOG_SMGR_TRUNCATE | XLR_SPECIAL_REL_UPDATE);
381 : :
382 : : /*
383 : : * Flush, because otherwise the truncation of the main relation might
384 : : * hit the disk before the WAL record, and the truncation of the FSM
385 : : * or visibility map. If we crashed during that window, we'd be left
386 : : * with a truncated heap, but the FSM or visibility map would still
387 : : * contain entries for the non-existent heap pages.
388 : : */
5611 389 [ + + - + ]: 192 : if (fsm || vm)
5625 390 : 155 : XLogFlush(lsn);
391 : : }
392 : :
393 : : /*
394 : : * This will first remove any buffers from the buffer pool that should no
395 : : * longer exist after truncation is complete, and then truncate the
396 : : * corresponding files on disk.
397 : : */
1007 tgl@sss.pgh.pa.us 398 : 524 : smgrtruncate(RelationGetSmgr(rel), forks, nforks, blocks);
399 : :
400 : : /* We've done all the critical work, so checkpoints are OK now. */
737 rhaas@postgresql.org 401 : 524 : MyProc->delayChkptFlags &= ~DELAY_CHKPT_COMPLETE;
402 : :
403 : : /*
404 : : * Update upper-level FSM pages to account for the truncation. This is
405 : : * important because the just-truncated pages were likely marked as
406 : : * all-free, and would be preferentially selected.
407 : : *
408 : : * NB: There's no point in delaying checkpoints until this is done.
409 : : * Because the FSM is not WAL-logged, we have to be prepared for the
410 : : * possibility of corruption after a crash anyway.
411 : : */
1664 fujii@postgresql.org 412 [ + + ]: 524 : if (need_fsm_vacuum)
413 : 165 : FreeSpaceMapVacuumRange(rel, nblocks, InvalidBlockNumber);
5625 heikki.linnakangas@i 414 : 524 : }
415 : :
416 : : /*
417 : : * RelationPreTruncate
418 : : * Perform AM-independent work before a physical truncation.
419 : : *
420 : : * If an access method's relation_nontransactional_truncate does not call
421 : : * RelationTruncate(), it must call this before decreasing the table size.
422 : : */
423 : : void
1471 noah@leadboat.com 424 : 524 : RelationPreTruncate(Relation rel)
425 : : {
426 : : PendingRelSync *pending;
427 : :
428 [ + + ]: 524 : if (!pendingSyncHash)
429 : 521 : return;
430 : :
1007 tgl@sss.pgh.pa.us 431 : 3 : pending = hash_search(pendingSyncHash,
648 rhaas@postgresql.org 432 : 3 : &(RelationGetSmgr(rel)->smgr_rlocator.locator),
433 : : HASH_FIND, NULL);
1471 noah@leadboat.com 434 [ + - ]: 3 : if (pending)
435 : 3 : pending->is_truncated = true;
436 : : }
437 : :
438 : : /*
439 : : * Copy a fork's data, block by block.
440 : : *
441 : : * Note that this requires that there is no dirty data in shared buffers. If
442 : : * it's possible that there are, callers need to flush those using
443 : : * e.g. FlushRelationBuffers(rel).
444 : : *
445 : : * Also note that this is frequently called via locutions such as
446 : : * RelationCopyStorage(RelationGetSmgr(rel), ...);
447 : : * That's safe only because we perform only smgr and WAL operations here.
448 : : * If we invoked anything else, a relcache flush could cause our SMgrRelation
449 : : * argument to become a dangling pointer.
450 : : */
451 : : void
1844 andres@anarazel.de 452 : 86 : RelationCopyStorage(SMgrRelation src, SMgrRelation dst,
453 : : ForkNumber forkNum, char relpersistence)
454 : : {
455 : : bool use_wal;
456 : : bool copying_initfork;
457 : : BlockNumber nblocks;
458 : : BlockNumber blkno;
459 : : BulkWriteState *bulkstate;
460 : :
461 : : /*
462 : : * The init fork for an unlogged relation in many respects has to be
463 : : * treated the same as normal relation, changes need to be WAL logged and
464 : : * it needs to be synced to disk.
465 : : */
466 [ - + - - ]: 86 : copying_initfork = relpersistence == RELPERSISTENCE_UNLOGGED &&
467 : : forkNum == INIT_FORKNUM;
468 : :
469 : : /*
470 : : * We need to log the copied data in WAL iff WAL archiving/streaming is
471 : : * enabled AND it's a permanent relation. This gives the same answer as
472 : : * "RelationNeedsWAL(rel) || copying_initfork", because we know the
473 : : * current operation created new relation storage.
474 : : */
475 [ + + + + ]: 94 : use_wal = XLogIsNeeded() &&
476 [ - + ]: 8 : (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork);
477 : :
51 heikki.linnakangas@i 478 :GNC 86 : bulkstate = smgr_bulk_start_smgr(dst, forkNum, use_wal);
479 : :
1844 andres@anarazel.de 480 :CBC 86 : nblocks = smgrnblocks(src, forkNum);
481 : :
482 [ + + ]: 680 : for (blkno = 0; blkno < nblocks; blkno++)
483 : : {
484 : : BulkWriteBuffer buf;
485 : :
486 : : /* If we got a cancel signal during the copy of the data, quit */
487 [ - + ]: 594 : CHECK_FOR_INTERRUPTS();
488 : :
51 heikki.linnakangas@i 489 :GNC 594 : buf = smgr_bulk_get_buf(bulkstate);
490 : 594 : smgrread(src, forkNum, blkno, (Page) buf);
491 : :
492 [ - + ]: 594 : if (!PageIsVerifiedExtended((Page) buf, blkno,
493 : : PIV_LOG_WARNING | PIV_REPORT_STAT))
494 : : {
495 : : /*
496 : : * For paranoia's sake, capture the file path before invoking the
497 : : * ereport machinery. This guards against the possibility of a
498 : : * relcache flush caused by, e.g., an errcontext callback.
499 : : * (errcontext callbacks shouldn't be risking any such thing, but
500 : : * people have been known to forget that rule.)
501 : : */
648 rhaas@postgresql.org 502 :UBC 0 : char *relpath = relpathbackend(src->smgr_rlocator.locator,
503 : : src->smgr_rlocator.backend,
504 : : forkNum);
505 : :
1844 andres@anarazel.de 506 [ # # ]: 0 : ereport(ERROR,
507 : : (errcode(ERRCODE_DATA_CORRUPTED),
508 : : errmsg("invalid page in block %u of relation %s",
509 : : blkno, relpath)));
510 : : }
511 : :
512 : : /*
513 : : * Queue the page for WAL-logging and writing out. Unfortunately we
514 : : * don't know what kind of a page this is, so we have to log the full
515 : : * page including any unused space.
516 : : */
51 heikki.linnakangas@i 517 :GNC 594 : smgr_bulk_write(bulkstate, blkno, buf, false);
518 : : }
519 : 86 : smgr_bulk_finish(bulkstate);
1844 andres@anarazel.de 520 :CBC 86 : }
521 : :
522 : : /*
523 : : * RelFileLocatorSkippingWAL
524 : : * Check if a BM_PERMANENT relfilelocator is using WAL.
525 : : *
526 : : * Changes to certain relations must not write WAL; see "Skipping WAL for
527 : : * New RelFileLocator" in src/backend/access/transam/README. Though it is
528 : : * known from Relation efficiently, this function is intended for the code
529 : : * paths not having access to Relation.
530 : : */
531 : : bool
648 rhaas@postgresql.org 532 : 956048 : RelFileLocatorSkippingWAL(RelFileLocator rlocator)
533 : : {
1471 noah@leadboat.com 534 [ + + + + ]: 1854524 : if (!pendingSyncHash ||
648 rhaas@postgresql.org 535 : 898476 : hash_search(pendingSyncHash, &rlocator, HASH_FIND, NULL) == NULL)
1471 noah@leadboat.com 536 : 943501 : return false;
537 : :
538 : 12547 : return true;
539 : : }
540 : :
541 : : /*
542 : : * EstimatePendingSyncsSpace
543 : : * Estimate space needed to pass syncs to parallel workers.
544 : : */
545 : : Size
546 : 414 : EstimatePendingSyncsSpace(void)
547 : : {
548 : : long entries;
549 : :
550 [ + + ]: 414 : entries = pendingSyncHash ? hash_get_num_entries(pendingSyncHash) : 0;
648 rhaas@postgresql.org 551 : 414 : return mul_size(1 + entries, sizeof(RelFileLocator));
552 : : }
553 : :
554 : : /*
555 : : * SerializePendingSyncs
556 : : * Serialize syncs for parallel workers.
557 : : */
558 : : void
1471 noah@leadboat.com 559 : 414 : SerializePendingSyncs(Size maxSize, char *startAddress)
560 : : {
561 : : HTAB *tmphash;
562 : : HASHCTL ctl;
563 : : HASH_SEQ_STATUS scan;
564 : : PendingRelSync *sync;
565 : : PendingRelDelete *delete;
566 : : RelFileLocator *src;
648 rhaas@postgresql.org 567 : 414 : RelFileLocator *dest = (RelFileLocator *) startAddress;
568 : :
1471 noah@leadboat.com 569 [ + + ]: 414 : if (!pendingSyncHash)
570 : 321 : goto terminate;
571 : :
572 : : /* Create temporary hash to collect active relfilelocators */
648 rhaas@postgresql.org 573 : 93 : ctl.keysize = sizeof(RelFileLocator);
574 : 93 : ctl.entrysize = sizeof(RelFileLocator);
1471 noah@leadboat.com 575 : 93 : ctl.hcxt = CurrentMemoryContext;
648 rhaas@postgresql.org 576 : 93 : tmphash = hash_create("tmp relfilelocators",
577 : : hash_get_num_entries(pendingSyncHash), &ctl,
578 : : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
579 : :
580 : : /* collect all rlocator from pending syncs */
1471 noah@leadboat.com 581 : 93 : hash_seq_init(&scan, pendingSyncHash);
582 [ + + ]: 814 : while ((sync = (PendingRelSync *) hash_seq_search(&scan)))
648 rhaas@postgresql.org 583 : 721 : (void) hash_search(tmphash, &sync->rlocator, HASH_ENTER, NULL);
584 : :
585 : : /* remove deleted rnodes */
1471 noah@leadboat.com 586 [ + + ]: 977 : for (delete = pendingDeletes; delete != NULL; delete = delete->next)
587 [ + + ]: 884 : if (delete->atCommit)
433 peter@eisentraut.org 588 : 158 : (void) hash_search(tmphash, &delete->rlocator,
589 : : HASH_REMOVE, NULL);
590 : :
1471 noah@leadboat.com 591 : 93 : hash_seq_init(&scan, tmphash);
648 rhaas@postgresql.org 592 [ + + ]: 660 : while ((src = (RelFileLocator *) hash_seq_search(&scan)))
1471 noah@leadboat.com 593 : 567 : *dest++ = *src;
594 : :
595 : 93 : hash_destroy(tmphash);
596 : :
597 : 414 : terminate:
648 rhaas@postgresql.org 598 [ + + - + : 414 : MemSet(dest, 0, sizeof(RelFileLocator));
- - - - -
- ]
1471 noah@leadboat.com 599 : 414 : }
600 : :
601 : : /*
602 : : * RestorePendingSyncs
603 : : * Restore syncs within a parallel worker.
604 : : *
605 : : * RelationNeedsWAL() and RelFileLocatorSkippingWAL() must offer the correct
606 : : * answer to parallel workers. Only smgrDoPendingSyncs() reads the
607 : : * is_truncated field, at end of transaction. Hence, don't restore it.
608 : : */
609 : : void
610 : 1322 : RestorePendingSyncs(char *startAddress)
611 : : {
612 : : RelFileLocator *rlocator;
613 : :
614 [ - + ]: 1322 : Assert(pendingSyncHash == NULL);
648 rhaas@postgresql.org 615 [ + + ]: 3114 : for (rlocator = (RelFileLocator *) startAddress; rlocator->relNumber != 0;
616 : 1792 : rlocator++)
617 : 1792 : AddPendingSync(rlocator);
1471 noah@leadboat.com 618 : 1322 : }
619 : :
620 : : /*
621 : : * smgrDoPendingDeletes() -- Take care of relation deletes at end of xact.
622 : : *
623 : : * This also runs when aborting a subxact; we want to clean up a failed
624 : : * subxact immediately.
625 : : *
626 : : * Note: It's possible that we're being asked to remove a relation that has
627 : : * no physical storage in any fork. In particular, it's possible that we're
628 : : * cleaning up an old temporary relation for which RemovePgTempFiles has
629 : : * already recovered the physical storage.
630 : : */
631 : : void
5625 heikki.linnakangas@i 632 : 435627 : smgrDoPendingDeletes(bool isCommit)
633 : : {
634 : 435627 : int nestLevel = GetCurrentTransactionNestLevel();
635 : : PendingRelDelete *pending;
636 : : PendingRelDelete *prev;
637 : : PendingRelDelete *next;
4105 alvherre@alvh.no-ip. 638 : 435627 : int nrels = 0,
3768 639 : 435627 : maxrels = 0;
640 : 435627 : SMgrRelation *srels = NULL;
641 : :
5625 heikki.linnakangas@i 642 : 435627 : prev = NULL;
643 [ + + ]: 527392 : for (pending = pendingDeletes; pending != NULL; pending = next)
644 : : {
645 : 91765 : next = pending->next;
646 [ + + ]: 91765 : if (pending->nestLevel < nestLevel)
647 : : {
648 : : /* outer-level entries should not be processed yet */
649 : 4034 : prev = pending;
650 : : }
651 : : else
652 : : {
653 : : /* unlink list entry first, so we don't retry on failure */
654 [ - + ]: 87731 : if (prev)
5625 heikki.linnakangas@i 655 :UBC 0 : prev->next = next;
656 : : else
5625 heikki.linnakangas@i 657 :CBC 87731 : pendingDeletes = next;
658 : : /* do deletion if called for */
659 [ + + ]: 87731 : if (pending->atCommit == isCommit)
660 : : {
661 : : SMgrRelation srel;
662 : :
42 heikki.linnakangas@i 663 :GNC 35426 : srel = smgropen(pending->rlocator, pending->procNumber);
664 : :
665 : : /* allocate the initial array, or extend it, if needed */
3768 alvherre@alvh.no-ip. 666 [ + + ]:CBC 35426 : if (maxrels == 0)
667 : : {
668 : 9978 : maxrels = 8;
3631 bruce@momjian.us 669 : 9978 : srels = palloc(sizeof(SMgrRelation) * maxrels);
670 : : }
3768 alvherre@alvh.no-ip. 671 [ + + ]: 25448 : else if (maxrels <= nrels)
672 : : {
4105 673 : 868 : maxrels *= 2;
674 : 868 : srels = repalloc(srels, sizeof(SMgrRelation) * maxrels);
675 : : }
676 : :
677 : 35426 : srels[nrels++] = srel;
678 : : }
679 : : /* must explicitly free the list entry */
5625 heikki.linnakangas@i 680 : 87731 : pfree(pending);
681 : : /* prev does not change */
682 : : }
683 : : }
684 : :
4105 alvherre@alvh.no-ip. 685 [ + + ]: 435627 : if (nrels > 0)
686 : : {
687 : 9978 : smgrdounlinkall(srels, nrels, false);
688 : :
1317 tgl@sss.pgh.pa.us 689 [ + + ]: 45404 : for (int i = 0; i < nrels; i++)
4105 alvherre@alvh.no-ip. 690 : 35426 : smgrclose(srels[i]);
691 : :
3768 692 : 9978 : pfree(srels);
693 : : }
5625 heikki.linnakangas@i 694 : 435627 : }
695 : :
696 : : /*
697 : : * smgrDoPendingSyncs() -- Take care of relation syncs at end of xact.
698 : : */
699 : : void
1471 noah@leadboat.com 700 : 431654 : smgrDoPendingSyncs(bool isCommit, bool isParallelWorker)
701 : : {
702 : : PendingRelDelete *pending;
703 : 431654 : int nrels = 0,
704 : 431654 : maxrels = 0;
705 : 431654 : SMgrRelation *srels = NULL;
706 : : HASH_SEQ_STATUS scan;
707 : : PendingRelSync *pendingsync;
708 : :
709 [ - + ]: 431654 : Assert(GetCurrentTransactionNestLevel() == 1);
710 : :
711 [ + + ]: 431654 : if (!pendingSyncHash)
712 : 426224 : return; /* no relation needs sync */
713 : :
714 : : /* Abort -- just throw away all pending syncs */
715 [ + + ]: 5869 : if (!isCommit)
716 : : {
717 : 242 : pendingSyncHash = NULL;
718 : 242 : return;
719 : : }
720 : :
721 : 5627 : AssertPendingSyncs_RelationCache();
722 : :
723 : : /* Parallel worker -- just throw away all pending syncs */
724 [ + + ]: 5627 : if (isParallelWorker)
725 : : {
726 : 197 : pendingSyncHash = NULL;
727 : 197 : return;
728 : : }
729 : :
730 : : /* Skip syncing nodes that smgrDoPendingDeletes() will delete. */
731 [ + + ]: 21125 : for (pending = pendingDeletes; pending != NULL; pending = pending->next)
732 [ + + ]: 15695 : if (pending->atCommit)
433 peter@eisentraut.org 733 : 3364 : (void) hash_search(pendingSyncHash, &pending->rlocator,
734 : : HASH_REMOVE, NULL);
735 : :
1471 noah@leadboat.com 736 : 5430 : hash_seq_init(&scan, pendingSyncHash);
737 [ + + ]: 38520 : while ((pendingsync = (PendingRelSync *) hash_seq_search(&scan)))
738 : : {
739 : : ForkNumber fork;
740 : : BlockNumber nblocks[MAX_FORKNUM + 1];
741 : 33090 : BlockNumber total_blocks = 0;
742 : : SMgrRelation srel;
743 : :
42 heikki.linnakangas@i 744 :GNC 33090 : srel = smgropen(pendingsync->rlocator, INVALID_PROC_NUMBER);
745 : :
746 : : /*
747 : : * We emit newpage WAL records for smaller relations.
748 : : *
749 : : * Small WAL records have a chance to be flushed along with other
750 : : * backends' WAL records. We emit WAL records instead of syncing for
751 : : * files that are smaller than a certain threshold, expecting faster
752 : : * commit. The threshold is defined by the GUC wal_skip_threshold.
753 : : */
1471 noah@leadboat.com 754 [ + - ]:CBC 33090 : if (!pendingsync->is_truncated)
755 : : {
756 [ + + ]: 165450 : for (fork = 0; fork <= MAX_FORKNUM; fork++)
757 : : {
758 [ + + ]: 132360 : if (smgrexists(srel, fork))
759 : : {
760 : 40151 : BlockNumber n = smgrnblocks(srel, fork);
761 : :
762 : : /* we shouldn't come here for unlogged relations */
763 [ - + ]: 40151 : Assert(fork != INIT_FORKNUM);
764 : 40151 : nblocks[fork] = n;
765 : 40151 : total_blocks += n;
766 : : }
767 : : else
768 : 92209 : nblocks[fork] = InvalidBlockNumber;
769 : : }
770 : : }
771 : :
772 : : /*
773 : : * Sync file or emit WAL records for its contents.
774 : : *
775 : : * Although we emit WAL record if the file is small enough, do file
776 : : * sync regardless of the size if the file has experienced a
777 : : * truncation. It is because the file would be followed by trailing
778 : : * garbage blocks after a crash recovery if, while a past longer file
779 : : * had been flushed out, we omitted syncing-out of the file and
780 : : * emitted WAL instead. You might think that we could choose WAL if
781 : : * the current main fork is longer than ever, but there's a case where
782 : : * main fork is longer than ever but FSM fork gets shorter.
783 : : */
784 [ + - ]: 33090 : if (pendingsync->is_truncated ||
785 [ + + ]: 33090 : total_blocks * BLCKSZ / 1024 >= wal_skip_threshold)
786 : : {
787 : : /* allocate the initial array, or extend it, if needed */
788 [ + - ]: 9 : if (maxrels == 0)
789 : : {
790 : 9 : maxrels = 8;
791 : 9 : srels = palloc(sizeof(SMgrRelation) * maxrels);
792 : : }
1471 noah@leadboat.com 793 [ # # ]:UBC 0 : else if (maxrels <= nrels)
794 : : {
795 : 0 : maxrels *= 2;
796 : 0 : srels = repalloc(srels, sizeof(SMgrRelation) * maxrels);
797 : : }
798 : :
1471 noah@leadboat.com 799 :CBC 9 : srels[nrels++] = srel;
800 : : }
801 : : else
802 : : {
803 : : /* Emit WAL records for all blocks. The file is small enough. */
804 [ + + ]: 165405 : for (fork = 0; fork <= MAX_FORKNUM; fork++)
805 : : {
806 : 132324 : int n = nblocks[fork];
807 : : Relation rel;
808 : :
809 [ + + ]: 132324 : if (!BlockNumberIsValid(n))
810 : 92183 : continue;
811 : :
812 : : /*
813 : : * Emit WAL for the whole file. Unfortunately we don't know
814 : : * what kind of a page this is, so we have to log the full
815 : : * page including any unused space. ReadBufferExtended()
816 : : * counts some pgstat events; unfortunately, we discard them.
817 : : */
648 rhaas@postgresql.org 818 : 40141 : rel = CreateFakeRelcacheEntry(srel->smgr_rlocator.locator);
1471 noah@leadboat.com 819 : 40141 : log_newpage_range(rel, fork, 0, n, false);
820 : 40141 : FreeFakeRelcacheEntry(rel);
821 : : }
822 : : }
823 : : }
824 : :
825 : 5430 : pendingSyncHash = NULL;
826 : :
827 [ + + ]: 5430 : if (nrels > 0)
828 : : {
829 : 9 : smgrdosyncall(srels, nrels);
830 : 9 : pfree(srels);
831 : : }
832 : : }
833 : :
834 : : /*
835 : : * smgrGetPendingDeletes() -- Get a list of non-temp relations to be deleted.
836 : : *
837 : : * The return value is the number of relations scheduled for termination.
838 : : * *ptr is set to point to a freshly-palloc'd array of RelFileLocators.
839 : : * If there are no relations to be deleted, *ptr is set to NULL.
840 : : *
841 : : * Only non-temporary relations are included in the returned list. This is OK
842 : : * because the list is used only in contexts where temporary relations don't
843 : : * matter: we're either writing to the two-phase state file (and transactions
844 : : * that have touched temp tables can't be prepared) or we're writing to xlog
845 : : * (and all temporary files will be zapped if we restart anyway, so no need
846 : : * for redo to do it also).
847 : : *
848 : : * Note that the list does not include anything scheduled for termination
849 : : * by upper-level transactions.
850 : : */
851 : : int
648 rhaas@postgresql.org 852 : 413119 : smgrGetPendingDeletes(bool forCommit, RelFileLocator **ptr)
853 : : {
5625 heikki.linnakangas@i 854 : 413119 : int nestLevel = GetCurrentTransactionNestLevel();
855 : : int nrels;
856 : : RelFileLocator *rptr;
857 : : PendingRelDelete *pending;
858 : :
859 : 413119 : nrels = 0;
860 [ + + ]: 503542 : for (pending = pendingDeletes; pending != NULL; pending = pending->next)
861 : : {
4993 rhaas@postgresql.org 862 [ + + + + ]: 90423 : if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit
42 heikki.linnakangas@i 863 [ + + ]:GNC 35486 : && pending->procNumber == INVALID_PROC_NUMBER)
5625 heikki.linnakangas@i 864 :CBC 32583 : nrels++;
865 : : }
866 [ + + ]: 413119 : if (nrels == 0)
867 : : {
868 : 403848 : *ptr = NULL;
869 : 403848 : return 0;
870 : : }
648 rhaas@postgresql.org 871 : 9271 : rptr = (RelFileLocator *) palloc(nrels * sizeof(RelFileLocator));
5625 heikki.linnakangas@i 872 : 9271 : *ptr = rptr;
873 [ + + ]: 49375 : for (pending = pendingDeletes; pending != NULL; pending = pending->next)
874 : : {
4993 rhaas@postgresql.org 875 [ + + + + ]: 40104 : if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit
42 heikki.linnakangas@i 876 [ + + ]:GNC 32642 : && pending->procNumber == INVALID_PROC_NUMBER)
877 : : {
648 rhaas@postgresql.org 878 :CBC 32583 : *rptr = pending->rlocator;
5625 heikki.linnakangas@i 879 : 32583 : rptr++;
880 : : }
881 : : }
882 : 9271 : return nrels;
883 : : }
884 : :
885 : : /*
886 : : * PostPrepare_smgr -- Clean up after a successful PREPARE
887 : : *
888 : : * What we have to do here is throw away the in-memory state about pending
889 : : * relation deletes. It's all been recorded in the 2PC state file and
890 : : * it's no longer smgr's job to worry about it.
891 : : */
892 : : void
893 : 391 : PostPrepare_smgr(void)
894 : : {
895 : : PendingRelDelete *pending;
896 : : PendingRelDelete *next;
897 : :
898 [ + + ]: 451 : for (pending = pendingDeletes; pending != NULL; pending = next)
899 : : {
900 : 60 : next = pending->next;
901 : 60 : pendingDeletes = next;
902 : : /* must explicitly free the list entry */
903 : 60 : pfree(pending);
904 : : }
905 : 391 : }
906 : :
907 : :
908 : : /*
909 : : * AtSubCommit_smgr() --- Take care of subtransaction commit.
910 : : *
911 : : * Reassign all items in the pending-deletes list to the parent transaction.
912 : : */
913 : : void
914 : 5361 : AtSubCommit_smgr(void)
915 : : {
916 : 5361 : int nestLevel = GetCurrentTransactionNestLevel();
917 : : PendingRelDelete *pending;
918 : :
919 [ + + ]: 5587 : for (pending = pendingDeletes; pending != NULL; pending = pending->next)
920 : : {
921 [ + + ]: 226 : if (pending->nestLevel >= nestLevel)
922 : 105 : pending->nestLevel = nestLevel - 1;
923 : : }
924 : 5361 : }
925 : :
926 : : /*
927 : : * AtSubAbort_smgr() --- Take care of subtransaction abort.
928 : : *
929 : : * Delete created relations and forget about deleted relations.
930 : : * We can execute these operations immediately because we know this
931 : : * subtransaction will not commit.
932 : : */
933 : : void
934 : 4566 : AtSubAbort_smgr(void)
935 : : {
936 : 4566 : smgrDoPendingDeletes(false);
937 : 4566 : }
938 : :
939 : : void
3433 940 : 18511 : smgr_redo(XLogReaderState *record)
941 : : {
942 : 18511 : XLogRecPtr lsn = record->EndRecPtr;
943 : 18511 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
944 : :
945 : : /* Backup blocks are not used in smgr records */
946 [ - + ]: 18511 : Assert(!XLogRecHasAnyBlockRefs(record));
947 : :
5625 948 [ + + ]: 18511 : if (info == XLOG_SMGR_CREATE)
949 : : {
950 : 18458 : xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
951 : : SMgrRelation reln;
952 : :
42 heikki.linnakangas@i 953 :GNC 18458 : reln = smgropen(xlrec->rlocator, INVALID_PROC_NUMBER);
4855 rhaas@postgresql.org 954 :CBC 18458 : smgrcreate(reln, xlrec->forkNum, true);
955 : : }
5625 heikki.linnakangas@i 956 [ + - ]: 53 : else if (info == XLOG_SMGR_TRUNCATE)
957 : : {
958 : 53 : xl_smgr_truncate *xlrec = (xl_smgr_truncate *) XLogRecGetData(record);
959 : : SMgrRelation reln;
960 : : Relation rel;
961 : : ForkNumber forks[MAX_FORKNUM];
962 : : BlockNumber blocks[MAX_FORKNUM];
1431 tgl@sss.pgh.pa.us 963 : 53 : int nforks = 0;
1664 fujii@postgresql.org 964 : 53 : bool need_fsm_vacuum = false;
965 : :
42 heikki.linnakangas@i 966 :GNC 53 : reln = smgropen(xlrec->rlocator, INVALID_PROC_NUMBER);
967 : :
968 : : /*
969 : : * Forcibly create relation if it doesn't exist (which suggests that
970 : : * it was dropped somewhere later in the WAL sequence). As in
971 : : * XLogReadBufferForRedo, we prefer to recreate the rel and replay the
972 : : * log as best we can until the drop is seen.
973 : : */
5625 heikki.linnakangas@i 974 :CBC 53 : smgrcreate(reln, MAIN_FORKNUM, true);
975 : :
976 : : /*
977 : : * Before we perform the truncation, update minimum recovery point to
978 : : * cover this WAL record. Once the relation is truncated, there's no
979 : : * going back. The buffer manager enforces the WAL-first rule for
980 : : * normal updates to relation files, so that the minimum recovery
981 : : * point is always updated before the corresponding change in the data
982 : : * file is flushed to disk. We have to do the same manually here.
983 : : *
984 : : * Doing this before the truncation means that if the truncation fails
985 : : * for some reason, you cannot start up the system even after restart,
986 : : * until you fix the underlying situation so that the truncation will
987 : : * succeed. Alternatively, we could update the minimum recovery point
988 : : * after truncation, but that would leave a small window where the
989 : : * WAL-first rule could be violated.
990 : : */
4143 991 : 53 : XLogFlush(lsn);
992 : :
993 : : /* Prepare for truncation of MAIN fork */
2858 rhaas@postgresql.org 994 [ + - ]: 53 : if ((xlrec->flags & SMGR_TRUNCATE_HEAP) != 0)
995 : : {
1664 fujii@postgresql.org 996 : 53 : forks[nforks] = MAIN_FORKNUM;
997 : 53 : blocks[nforks] = xlrec->blkno;
998 : 53 : nforks++;
999 : :
1000 : : /* Also tell xlogutils.c about it */
648 rhaas@postgresql.org 1001 : 53 : XLogTruncateRelation(xlrec->rlocator, MAIN_FORKNUM, xlrec->blkno);
1002 : : }
1003 : :
1004 : : /* Prepare for truncation of FSM and VM too */
1005 : 53 : rel = CreateFakeRelcacheEntry(xlrec->rlocator);
1006 : :
2858 1007 [ + - + + ]: 106 : if ((xlrec->flags & SMGR_TRUNCATE_FSM) != 0 &&
1008 : 53 : smgrexists(reln, FSM_FORKNUM))
1009 : : {
1664 fujii@postgresql.org 1010 : 35 : blocks[nforks] = FreeSpaceMapPrepareTruncateRel(rel, xlrec->blkno);
1011 [ + - ]: 35 : if (BlockNumberIsValid(blocks[nforks]))
1012 : : {
1013 : 35 : forks[nforks] = FSM_FORKNUM;
1014 : 35 : nforks++;
1015 : 35 : need_fsm_vacuum = true;
1016 : : }
1017 : : }
2858 rhaas@postgresql.org 1018 [ + - + + ]: 106 : if ((xlrec->flags & SMGR_TRUNCATE_VM) != 0 &&
1019 : 53 : smgrexists(reln, VISIBILITYMAP_FORKNUM))
1020 : : {
1664 fujii@postgresql.org 1021 : 30 : blocks[nforks] = visibilitymap_prepare_truncate(rel, xlrec->blkno);
1022 [ + + ]: 30 : if (BlockNumberIsValid(blocks[nforks]))
1023 : : {
1024 : 13 : forks[nforks] = VISIBILITYMAP_FORKNUM;
1025 : 13 : nforks++;
1026 : : }
1027 : : }
1028 : :
1029 : : /* Do the real work to truncate relation forks */
1030 [ + - ]: 53 : if (nforks > 0)
1031 : 53 : smgrtruncate(reln, forks, nforks, blocks);
1032 : :
1033 : : /*
1034 : : * Update upper-level FSM pages to account for the truncation. This is
1035 : : * important because the just-truncated pages were likely marked as
1036 : : * all-free, and would be preferentially selected.
1037 : : */
1038 [ + + ]: 53 : if (need_fsm_vacuum)
1039 : 35 : FreeSpaceMapVacuumRange(rel, xlrec->blkno,
1040 : : InvalidBlockNumber);
1041 : :
5178 tgl@sss.pgh.pa.us 1042 : 53 : FreeFakeRelcacheEntry(rel);
1043 : : }
1044 : : else
5625 heikki.linnakangas@i 1045 [ # # ]:UBC 0 : elog(PANIC, "smgr_redo: unknown op code %u", info);
5625 heikki.linnakangas@i 1046 :CBC 18511 : }
|