Line data Source code
1 : /*
2 : * brin_revmap.c
3 : * Range map for BRIN indexes
4 : *
5 : * The range map (revmap) is a translation structure for BRIN indexes: for each
6 : * page range there is one summary tuple, and its location is tracked by the
7 : * revmap. Whenever a new tuple is inserted into a table that violates the
8 : * previously recorded summary values, a new tuple is inserted into the index
9 : * and the revmap is updated to point to it.
10 : *
11 : * The revmap is stored in the first pages of the index, immediately following
12 : * the metapage. When the revmap needs to be expanded, all tuples on the
13 : * regular BRIN page at that block (if any) are moved out of the way.
14 : *
15 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
16 : * Portions Copyright (c) 1994, Regents of the University of California
17 : *
18 : * IDENTIFICATION
19 : * src/backend/access/brin/brin_revmap.c
20 : */
21 : #include "postgres.h"
22 :
23 : #include "access/brin_page.h"
24 : #include "access/brin_pageops.h"
25 : #include "access/brin_revmap.h"
26 : #include "access/brin_tuple.h"
27 : #include "access/brin_xlog.h"
28 : #include "access/rmgr.h"
29 : #include "access/xloginsert.h"
30 : #include "miscadmin.h"
31 : #include "storage/bufmgr.h"
32 : #include "utils/rel.h"
33 :
34 :
35 : /*
36 : * In revmap pages, each item stores an ItemPointerData. These defines let one
37 : * find the logical revmap page number and index number of the revmap item for
38 : * the given heap block number.
39 : */
40 : #define HEAPBLK_TO_REVMAP_BLK(pagesPerRange, heapBlk) \
41 : ((heapBlk / pagesPerRange) / REVMAP_PAGE_MAXITEMS)
42 : #define HEAPBLK_TO_REVMAP_INDEX(pagesPerRange, heapBlk) \
43 : ((heapBlk / pagesPerRange) % REVMAP_PAGE_MAXITEMS)
44 :
45 :
46 : struct BrinRevmap
47 : {
48 : Relation rm_irel;
49 : BlockNumber rm_pagesPerRange;
50 : BlockNumber rm_lastRevmapPage; /* cached from the metapage */
51 : Buffer rm_metaBuf;
52 : Buffer rm_currBuf;
53 : };
54 :
55 : /* typedef appears in brin_revmap.h */
56 :
57 :
58 : static BlockNumber revmap_get_blkno(BrinRevmap *revmap,
59 : BlockNumber heapBlk);
60 : static Buffer revmap_get_buffer(BrinRevmap *revmap, BlockNumber heapBlk);
61 : static BlockNumber revmap_extend_and_get_blkno(BrinRevmap *revmap,
62 : BlockNumber heapBlk);
63 : static void revmap_physical_extend(BrinRevmap *revmap);
64 :
65 : /*
66 : * Initialize an access object for a range map. This must be freed by
67 : * brinRevmapTerminate when caller is done with it.
68 : */
69 : BrinRevmap *
70 4684 : brinRevmapInitialize(Relation idxrel, BlockNumber *pagesPerRange)
71 : {
72 : BrinRevmap *revmap;
73 : Buffer meta;
74 : BrinMetaPageData *metadata;
75 : Page page;
76 :
77 4684 : meta = ReadBuffer(idxrel, BRIN_METAPAGE_BLKNO);
78 4684 : LockBuffer(meta, BUFFER_LOCK_SHARE);
79 4684 : page = BufferGetPage(meta);
80 4684 : metadata = (BrinMetaPageData *) PageGetContents(page);
81 :
82 4684 : revmap = palloc(sizeof(BrinRevmap));
83 4684 : revmap->rm_irel = idxrel;
84 4684 : revmap->rm_pagesPerRange = metadata->pagesPerRange;
85 4684 : revmap->rm_lastRevmapPage = metadata->lastRevmapPage;
86 4684 : revmap->rm_metaBuf = meta;
87 4684 : revmap->rm_currBuf = InvalidBuffer;
88 :
89 4684 : *pagesPerRange = metadata->pagesPerRange;
90 :
91 4684 : LockBuffer(meta, BUFFER_LOCK_UNLOCK);
92 :
93 4684 : return revmap;
94 : }
95 :
96 : /*
97 : * Release resources associated with a revmap access object.
98 : */
99 : void
100 4684 : brinRevmapTerminate(BrinRevmap *revmap)
101 : {
102 4684 : ReleaseBuffer(revmap->rm_metaBuf);
103 4684 : if (revmap->rm_currBuf != InvalidBuffer)
104 4636 : ReleaseBuffer(revmap->rm_currBuf);
105 4684 : pfree(revmap);
106 4684 : }
107 :
108 : /*
109 : * Extend the revmap to cover the given heap block number.
110 : */
111 : void
112 33050 : brinRevmapExtend(BrinRevmap *revmap, BlockNumber heapBlk)
113 : {
114 : BlockNumber mapBlk PG_USED_FOR_ASSERTS_ONLY;
115 :
116 33050 : mapBlk = revmap_extend_and_get_blkno(revmap, heapBlk);
117 :
118 : /* Ensure the buffer we got is in the expected range */
119 : Assert(mapBlk != InvalidBlockNumber &&
120 : mapBlk != BRIN_METAPAGE_BLKNO &&
121 : mapBlk <= revmap->rm_lastRevmapPage);
122 33050 : }
123 :
124 : /*
125 : * Prepare to insert an entry into the revmap; the revmap buffer in which the
126 : * entry is to reside is locked and returned. Most callers should call
127 : * brinRevmapExtend beforehand, as this routine does not extend the revmap if
128 : * it's not long enough.
129 : *
130 : * The returned buffer is also recorded in the revmap struct; finishing that
131 : * releases the buffer, therefore the caller needn't do it explicitly.
132 : */
133 : Buffer
134 6308 : brinLockRevmapPageForUpdate(BrinRevmap *revmap, BlockNumber heapBlk)
135 : {
136 : Buffer rmBuf;
137 :
138 6308 : rmBuf = revmap_get_buffer(revmap, heapBlk);
139 6308 : LockBuffer(rmBuf, BUFFER_LOCK_EXCLUSIVE);
140 :
141 6308 : return rmBuf;
142 : }
143 :
144 : /*
145 : * In the given revmap buffer (locked appropriately by caller), which is used
146 : * in a BRIN index of pagesPerRange pages per range, set the element
147 : * corresponding to heap block number heapBlk to the given TID.
148 : *
149 : * Once the operation is complete, the caller must update the LSN on the
150 : * returned buffer.
151 : *
152 : * This is used both in regular operation and during WAL replay.
153 : */
154 : void
155 7150 : brinSetHeapBlockItemptr(Buffer buf, BlockNumber pagesPerRange,
156 : BlockNumber heapBlk, ItemPointerData tid)
157 : {
158 : RevmapContents *contents;
159 : ItemPointerData *iptr;
160 : Page page;
161 :
162 : /* The correct page should already be pinned and locked */
163 7150 : page = BufferGetPage(buf);
164 7150 : contents = (RevmapContents *) PageGetContents(page);
165 7150 : iptr = (ItemPointerData *) contents->rm_tids;
166 7150 : iptr += HEAPBLK_TO_REVMAP_INDEX(pagesPerRange, heapBlk);
167 :
168 7150 : if (ItemPointerIsValid(&tid))
169 7094 : ItemPointerSet(iptr,
170 : ItemPointerGetBlockNumber(&tid),
171 7094 : ItemPointerGetOffsetNumber(&tid));
172 : else
173 56 : ItemPointerSetInvalid(iptr);
174 7150 : }
175 :
176 : /*
177 : * Fetch the BrinTuple for a given heap block.
178 : *
179 : * The buffer containing the tuple is locked, and returned in *buf. The
180 : * returned tuple points to the shared buffer and must not be freed; if caller
181 : * wants to use it after releasing the buffer lock, it must create its own
182 : * palloc'ed copy. As an optimization, the caller can pass a pinned buffer
183 : * *buf on entry, which will avoid a pin-unpin cycle when the next tuple is on
184 : * the same page as a previous one.
185 : *
186 : * If no tuple is found for the given heap range, returns NULL. In that case,
187 : * *buf might still be updated (and pin must be released by caller), but it's
188 : * not locked.
189 : *
190 : * The output tuple offset within the buffer is returned in *off, and its size
191 : * is returned in *size.
192 : */
193 : BrinTuple *
194 336312 : brinGetTupleForHeapBlock(BrinRevmap *revmap, BlockNumber heapBlk,
195 : Buffer *buf, OffsetNumber *off, Size *size, int mode)
196 : {
197 336312 : Relation idxRel = revmap->rm_irel;
198 : BlockNumber mapBlk;
199 : RevmapContents *contents;
200 : ItemPointerData *iptr;
201 : BlockNumber blk;
202 : Page page;
203 : ItemId lp;
204 : BrinTuple *tup;
205 : ItemPointerData previptr;
206 :
207 : /* normalize the heap block number to be the first page in the range */
208 336312 : heapBlk = (heapBlk / revmap->rm_pagesPerRange) * revmap->rm_pagesPerRange;
209 :
210 : /*
211 : * Compute the revmap page number we need. If Invalid is returned (i.e.,
212 : * the revmap page hasn't been created yet), the requested page range is
213 : * not summarized.
214 : */
215 336312 : mapBlk = revmap_get_blkno(revmap, heapBlk);
216 336312 : if (mapBlk == InvalidBlockNumber)
217 : {
218 1882 : *off = InvalidOffsetNumber;
219 1882 : return NULL;
220 : }
221 :
222 334430 : ItemPointerSetInvalid(&previptr);
223 : for (;;)
224 : {
225 334430 : CHECK_FOR_INTERRUPTS();
226 :
227 664636 : if (revmap->rm_currBuf == InvalidBuffer ||
228 330206 : BufferGetBlockNumber(revmap->rm_currBuf) != mapBlk)
229 : {
230 4224 : if (revmap->rm_currBuf != InvalidBuffer)
231 0 : ReleaseBuffer(revmap->rm_currBuf);
232 :
233 : Assert(mapBlk != InvalidBlockNumber);
234 4224 : revmap->rm_currBuf = ReadBuffer(revmap->rm_irel, mapBlk);
235 : }
236 :
237 334430 : LockBuffer(revmap->rm_currBuf, BUFFER_LOCK_SHARE);
238 :
239 : contents = (RevmapContents *)
240 334430 : PageGetContents(BufferGetPage(revmap->rm_currBuf));
241 334430 : iptr = contents->rm_tids;
242 334430 : iptr += HEAPBLK_TO_REVMAP_INDEX(revmap->rm_pagesPerRange, heapBlk);
243 :
244 334430 : if (!ItemPointerIsValid(iptr))
245 : {
246 80894 : LockBuffer(revmap->rm_currBuf, BUFFER_LOCK_UNLOCK);
247 80894 : return NULL;
248 : }
249 :
250 : /*
251 : * Check the TID we got in a previous iteration, if any, and save the
252 : * current TID we got from the revmap; if we loop, we can sanity-check
253 : * that the next one we get is different. Otherwise we might be stuck
254 : * looping forever if the revmap is somehow badly broken.
255 : */
256 253536 : if (ItemPointerIsValid(&previptr) && ItemPointerEquals(&previptr, iptr))
257 0 : ereport(ERROR,
258 : (errcode(ERRCODE_INDEX_CORRUPTED),
259 : errmsg_internal("corrupted BRIN index: inconsistent range map")));
260 253536 : previptr = *iptr;
261 :
262 253536 : blk = ItemPointerGetBlockNumber(iptr);
263 253536 : *off = ItemPointerGetOffsetNumber(iptr);
264 :
265 253536 : LockBuffer(revmap->rm_currBuf, BUFFER_LOCK_UNLOCK);
266 :
267 : /* Ok, got a pointer to where the BrinTuple should be. Fetch it. */
268 253536 : if (!BufferIsValid(*buf) || BufferGetBlockNumber(*buf) != blk)
269 : {
270 68494 : if (BufferIsValid(*buf))
271 17588 : ReleaseBuffer(*buf);
272 68494 : *buf = ReadBuffer(idxRel, blk);
273 : }
274 253536 : LockBuffer(*buf, mode);
275 253536 : page = BufferGetPage(*buf);
276 :
277 : /* If we land on a revmap page, start over */
278 253536 : if (BRIN_IS_REGULAR_PAGE(page))
279 : {
280 : /*
281 : * If the offset number is greater than what's in the page, it's
282 : * possible that the range was desummarized concurrently. Just
283 : * return NULL to handle that case.
284 : */
285 253536 : if (*off > PageGetMaxOffsetNumber(page))
286 : {
287 0 : LockBuffer(*buf, BUFFER_LOCK_UNLOCK);
288 0 : return NULL;
289 : }
290 :
291 253536 : lp = PageGetItemId(page, *off);
292 253536 : if (ItemIdIsUsed(lp))
293 : {
294 253536 : tup = (BrinTuple *) PageGetItem(page, lp);
295 :
296 253536 : if (tup->bt_blkno == heapBlk)
297 : {
298 253536 : if (size)
299 189936 : *size = ItemIdGetLength(lp);
300 : /* found it! */
301 253536 : return tup;
302 : }
303 : }
304 : }
305 :
306 : /*
307 : * No luck. Assume that the revmap was updated concurrently.
308 : */
309 0 : LockBuffer(*buf, BUFFER_LOCK_UNLOCK);
310 : }
311 : /* not reached, but keep compiler quiet */
312 : return NULL;
313 : }
314 :
315 : /*
316 : * Delete an index tuple, marking a page range as unsummarized.
317 : *
318 : * Index must be locked in ShareUpdateExclusiveLock mode.
319 : *
320 : * Return false if caller should retry.
321 : */
322 : bool
323 86 : brinRevmapDesummarizeRange(Relation idxrel, BlockNumber heapBlk)
324 : {
325 : BrinRevmap *revmap;
326 : BlockNumber pagesPerRange;
327 : RevmapContents *contents;
328 : ItemPointerData *iptr;
329 : ItemPointerData invalidIptr;
330 : BlockNumber revmapBlk;
331 : Buffer revmapBuf;
332 : Buffer regBuf;
333 : Page revmapPg;
334 : Page regPg;
335 : OffsetNumber revmapOffset;
336 : OffsetNumber regOffset;
337 : ItemId lp;
338 :
339 86 : revmap = brinRevmapInitialize(idxrel, &pagesPerRange);
340 :
341 86 : revmapBlk = revmap_get_blkno(revmap, heapBlk);
342 86 : if (!BlockNumberIsValid(revmapBlk))
343 : {
344 : /* revmap page doesn't exist: range not summarized, we're done */
345 18 : brinRevmapTerminate(revmap);
346 18 : return true;
347 : }
348 :
349 : /* Lock the revmap page, obtain the index tuple pointer from it */
350 68 : revmapBuf = brinLockRevmapPageForUpdate(revmap, heapBlk);
351 68 : revmapPg = BufferGetPage(revmapBuf);
352 68 : revmapOffset = HEAPBLK_TO_REVMAP_INDEX(revmap->rm_pagesPerRange, heapBlk);
353 :
354 68 : contents = (RevmapContents *) PageGetContents(revmapPg);
355 68 : iptr = contents->rm_tids;
356 68 : iptr += revmapOffset;
357 :
358 68 : if (!ItemPointerIsValid(iptr))
359 : {
360 : /* no index tuple: range not summarized, we're done */
361 24 : LockBuffer(revmapBuf, BUFFER_LOCK_UNLOCK);
362 24 : brinRevmapTerminate(revmap);
363 24 : return true;
364 : }
365 :
366 44 : regBuf = ReadBuffer(idxrel, ItemPointerGetBlockNumber(iptr));
367 44 : LockBuffer(regBuf, BUFFER_LOCK_EXCLUSIVE);
368 44 : regPg = BufferGetPage(regBuf);
369 :
370 : /* if this is no longer a regular page, tell caller to start over */
371 44 : if (!BRIN_IS_REGULAR_PAGE(regPg))
372 : {
373 0 : LockBuffer(revmapBuf, BUFFER_LOCK_UNLOCK);
374 0 : LockBuffer(regBuf, BUFFER_LOCK_UNLOCK);
375 0 : brinRevmapTerminate(revmap);
376 0 : return false;
377 : }
378 :
379 44 : regOffset = ItemPointerGetOffsetNumber(iptr);
380 44 : if (regOffset > PageGetMaxOffsetNumber(regPg))
381 0 : ereport(ERROR,
382 : (errcode(ERRCODE_INDEX_CORRUPTED),
383 : errmsg("corrupted BRIN index: inconsistent range map")));
384 :
385 44 : lp = PageGetItemId(regPg, regOffset);
386 44 : if (!ItemIdIsUsed(lp))
387 0 : ereport(ERROR,
388 : (errcode(ERRCODE_INDEX_CORRUPTED),
389 : errmsg("corrupted BRIN index: inconsistent range map")));
390 :
391 : /*
392 : * Placeholder tuples only appear during unfinished summarization, and we
393 : * hold ShareUpdateExclusiveLock, so this function cannot run concurrently
394 : * with that. So any placeholder tuples that exist are leftovers from a
395 : * crashed or aborted summarization; remove them silently.
396 : */
397 :
398 44 : START_CRIT_SECTION();
399 :
400 44 : ItemPointerSetInvalid(&invalidIptr);
401 44 : brinSetHeapBlockItemptr(revmapBuf, revmap->rm_pagesPerRange, heapBlk,
402 : invalidIptr);
403 44 : PageIndexTupleDeleteNoCompact(regPg, regOffset);
404 : /* XXX record free space in FSM? */
405 :
406 44 : MarkBufferDirty(regBuf);
407 44 : MarkBufferDirty(revmapBuf);
408 :
409 44 : if (RelationNeedsWAL(idxrel))
410 : {
411 : xl_brin_desummarize xlrec;
412 : XLogRecPtr recptr;
413 :
414 44 : xlrec.pagesPerRange = revmap->rm_pagesPerRange;
415 44 : xlrec.heapBlk = heapBlk;
416 44 : xlrec.regOffset = regOffset;
417 :
418 44 : XLogBeginInsert();
419 44 : XLogRegisterData((char *) &xlrec, SizeOfBrinDesummarize);
420 44 : XLogRegisterBuffer(0, revmapBuf, 0);
421 44 : XLogRegisterBuffer(1, regBuf, REGBUF_STANDARD);
422 44 : recptr = XLogInsert(RM_BRIN_ID, XLOG_BRIN_DESUMMARIZE);
423 44 : PageSetLSN(revmapPg, recptr);
424 44 : PageSetLSN(regPg, recptr);
425 : }
426 :
427 44 : END_CRIT_SECTION();
428 :
429 44 : UnlockReleaseBuffer(regBuf);
430 44 : LockBuffer(revmapBuf, BUFFER_LOCK_UNLOCK);
431 44 : brinRevmapTerminate(revmap);
432 :
433 44 : return true;
434 : }
435 :
436 : /*
437 : * Given a heap block number, find the corresponding physical revmap block
438 : * number and return it. If the revmap page hasn't been allocated yet, return
439 : * InvalidBlockNumber.
440 : */
441 : static BlockNumber
442 342706 : revmap_get_blkno(BrinRevmap *revmap, BlockNumber heapBlk)
443 : {
444 : BlockNumber targetblk;
445 :
446 : /* obtain revmap block number, skip 1 for metapage block */
447 342706 : targetblk = HEAPBLK_TO_REVMAP_BLK(revmap->rm_pagesPerRange, heapBlk) + 1;
448 :
449 : /* Normal case: the revmap page is already allocated */
450 342706 : if (targetblk <= revmap->rm_lastRevmapPage)
451 340806 : return targetblk;
452 :
453 1900 : return InvalidBlockNumber;
454 : }
455 :
456 : /*
457 : * Obtain and return a buffer containing the revmap page for the given heap
458 : * page. The revmap must have been previously extended to cover that page.
459 : * The returned buffer is also recorded in the revmap struct; finishing that
460 : * releases the buffer, therefore the caller needn't do it explicitly.
461 : */
462 : static Buffer
463 6308 : revmap_get_buffer(BrinRevmap *revmap, BlockNumber heapBlk)
464 : {
465 : BlockNumber mapBlk;
466 :
467 : /* Translate the heap block number to physical index location. */
468 6308 : mapBlk = revmap_get_blkno(revmap, heapBlk);
469 :
470 6308 : if (mapBlk == InvalidBlockNumber)
471 0 : elog(ERROR, "revmap does not cover heap block %u", heapBlk);
472 :
473 : /* Ensure the buffer we got is in the expected range */
474 : Assert(mapBlk != BRIN_METAPAGE_BLKNO &&
475 : mapBlk <= revmap->rm_lastRevmapPage);
476 :
477 : /*
478 : * Obtain the buffer from which we need to read. If we already have the
479 : * correct buffer in our access struct, use that; otherwise, release that,
480 : * (if valid) and read the one we need.
481 : */
482 12204 : if (revmap->rm_currBuf == InvalidBuffer ||
483 5896 : mapBlk != BufferGetBlockNumber(revmap->rm_currBuf))
484 : {
485 414 : if (revmap->rm_currBuf != InvalidBuffer)
486 2 : ReleaseBuffer(revmap->rm_currBuf);
487 :
488 414 : revmap->rm_currBuf = ReadBuffer(revmap->rm_irel, mapBlk);
489 : }
490 :
491 6308 : return revmap->rm_currBuf;
492 : }
493 :
494 : /*
495 : * Given a heap block number, find the corresponding physical revmap block
496 : * number and return it. If the revmap page hasn't been allocated yet, extend
497 : * the revmap until it is.
498 : */
499 : static BlockNumber
500 33050 : revmap_extend_and_get_blkno(BrinRevmap *revmap, BlockNumber heapBlk)
501 : {
502 : BlockNumber targetblk;
503 :
504 : /* obtain revmap block number, skip 1 for metapage block */
505 33050 : targetblk = HEAPBLK_TO_REVMAP_BLK(revmap->rm_pagesPerRange, heapBlk) + 1;
506 :
507 : /* Extend the revmap, if necessary */
508 33744 : while (targetblk > revmap->rm_lastRevmapPage)
509 : {
510 694 : CHECK_FOR_INTERRUPTS();
511 694 : revmap_physical_extend(revmap);
512 : }
513 :
514 33050 : return targetblk;
515 : }
516 :
517 : /*
518 : * Try to extend the revmap by one page. This might not happen for a number of
519 : * reasons; caller is expected to retry until the expected outcome is obtained.
520 : */
521 : static void
522 694 : revmap_physical_extend(BrinRevmap *revmap)
523 : {
524 : Buffer buf;
525 : Page page;
526 : Page metapage;
527 : BrinMetaPageData *metadata;
528 : BlockNumber mapBlk;
529 : BlockNumber nblocks;
530 694 : Relation irel = revmap->rm_irel;
531 :
532 : /*
533 : * Lock the metapage. This locks out concurrent extensions of the revmap,
534 : * but note that we still need to grab the relation extension lock because
535 : * another backend can extend the index with regular BRIN pages.
536 : */
537 694 : LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_EXCLUSIVE);
538 694 : metapage = BufferGetPage(revmap->rm_metaBuf);
539 694 : metadata = (BrinMetaPageData *) PageGetContents(metapage);
540 :
541 : /*
542 : * Check that our cached lastRevmapPage value was up-to-date; if it
543 : * wasn't, update the cached copy and have caller start over.
544 : */
545 694 : if (metadata->lastRevmapPage != revmap->rm_lastRevmapPage)
546 : {
547 346 : revmap->rm_lastRevmapPage = metadata->lastRevmapPage;
548 346 : LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_UNLOCK);
549 346 : return;
550 : }
551 348 : mapBlk = metadata->lastRevmapPage + 1;
552 :
553 348 : nblocks = RelationGetNumberOfBlocks(irel);
554 348 : if (mapBlk < nblocks)
555 : {
556 4 : buf = ReadBuffer(irel, mapBlk);
557 4 : LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
558 4 : page = BufferGetPage(buf);
559 : }
560 : else
561 : {
562 344 : buf = ExtendBufferedRel(BMR_REL(irel), MAIN_FORKNUM, NULL,
563 : EB_LOCK_FIRST);
564 344 : if (BufferGetBlockNumber(buf) != mapBlk)
565 : {
566 : /*
567 : * Very rare corner case: somebody extended the relation
568 : * concurrently after we read its length. If this happens, give
569 : * up and have caller start over. We will have to evacuate that
570 : * page from under whoever is using it.
571 : */
572 0 : LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_UNLOCK);
573 0 : UnlockReleaseBuffer(buf);
574 0 : return;
575 : }
576 344 : page = BufferGetPage(buf);
577 : }
578 :
579 : /* Check that it's a regular block (or an empty page) */
580 348 : if (!PageIsNew(page) && !BRIN_IS_REGULAR_PAGE(page))
581 0 : ereport(ERROR,
582 : (errcode(ERRCODE_INDEX_CORRUPTED),
583 : errmsg("unexpected page type 0x%04X in BRIN index \"%s\" block %u",
584 : BrinPageType(page),
585 : RelationGetRelationName(irel),
586 : BufferGetBlockNumber(buf))));
587 :
588 : /* If the page is in use, evacuate it and restart */
589 348 : if (brin_start_evacuating_page(irel, buf))
590 : {
591 2 : LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_UNLOCK);
592 2 : brin_evacuate_page(irel, revmap->rm_pagesPerRange, revmap, buf);
593 :
594 : /* have caller start over */
595 2 : return;
596 : }
597 :
598 : /*
599 : * Ok, we have now locked the metapage and the target block. Re-initialize
600 : * the target block as a revmap page, and update the metapage.
601 : */
602 346 : START_CRIT_SECTION();
603 :
604 : /* the rm_tids array is initialized to all invalid by PageInit */
605 346 : brin_page_init(page, BRIN_PAGETYPE_REVMAP);
606 346 : MarkBufferDirty(buf);
607 :
608 346 : metadata->lastRevmapPage = mapBlk;
609 :
610 : /*
611 : * Set pd_lower just past the end of the metadata. This is essential,
612 : * because without doing so, metadata will be lost if xlog.c compresses
613 : * the page. (We must do this here because pre-v11 versions of PG did not
614 : * set the metapage's pd_lower correctly, so a pg_upgraded index might
615 : * contain the wrong value.)
616 : */
617 346 : ((PageHeader) metapage)->pd_lower =
618 346 : ((char *) metadata + sizeof(BrinMetaPageData)) - (char *) metapage;
619 :
620 346 : MarkBufferDirty(revmap->rm_metaBuf);
621 :
622 346 : if (RelationNeedsWAL(revmap->rm_irel))
623 : {
624 : xl_brin_revmap_extend xlrec;
625 : XLogRecPtr recptr;
626 :
627 180 : xlrec.targetBlk = mapBlk;
628 :
629 180 : XLogBeginInsert();
630 180 : XLogRegisterData((char *) &xlrec, SizeOfBrinRevmapExtend);
631 180 : XLogRegisterBuffer(0, revmap->rm_metaBuf, REGBUF_STANDARD);
632 :
633 180 : XLogRegisterBuffer(1, buf, REGBUF_WILL_INIT);
634 :
635 180 : recptr = XLogInsert(RM_BRIN_ID, XLOG_BRIN_REVMAP_EXTEND);
636 180 : PageSetLSN(metapage, recptr);
637 180 : PageSetLSN(page, recptr);
638 : }
639 :
640 346 : END_CRIT_SECTION();
641 :
642 346 : LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_UNLOCK);
643 :
644 346 : UnlockReleaseBuffer(buf);
645 : }
|