@@ -168,9 +168,12 @@ brinSetHeapBlockItemptr(Buffer buf, BlockNumber pagesPerRange,
168168 iptr = (ItemPointerData * ) contents -> rm_tids ;
169169 iptr += HEAPBLK_TO_REVMAP_INDEX (pagesPerRange , heapBlk );
170170
171- ItemPointerSet (iptr ,
172- ItemPointerGetBlockNumber (& tid ),
173- ItemPointerGetOffsetNumber (& tid ));
171+ if (ItemPointerIsValid (& tid ))
172+ ItemPointerSet (iptr ,
173+ ItemPointerGetBlockNumber (& tid ),
174+ ItemPointerGetOffsetNumber (& tid ));
175+ else
176+ ItemPointerSetInvalid (iptr );
174177}
175178
176179/*
@@ -304,6 +307,137 @@ brinGetTupleForHeapBlock(BrinRevmap *revmap, BlockNumber heapBlk,
304307 return NULL ;
305308}
306309
310+ /*
311+ * Delete an index tuple, marking a page range as unsummarized.
312+ *
313+ * Index must be locked in ShareUpdateExclusiveLock mode.
314+ *
315+ * Return FALSE if caller should retry.
316+ */
317+ bool
318+ brinRevmapDesummarizeRange (Relation idxrel , BlockNumber heapBlk )
319+ {
320+ BrinRevmap * revmap ;
321+ BlockNumber pagesPerRange ;
322+ RevmapContents * contents ;
323+ ItemPointerData * iptr ;
324+ ItemPointerData invalidIptr ;
325+ BlockNumber revmapBlk ;
326+ Buffer revmapBuf ;
327+ Buffer regBuf ;
328+ Page revmapPg ;
329+ Page regPg ;
330+ OffsetNumber revmapOffset ;
331+ OffsetNumber regOffset ;
332+ ItemId lp ;
333+ BrinTuple * tup ;
334+
335+ revmap = brinRevmapInitialize (idxrel , & pagesPerRange , NULL );
336+
337+ revmapBlk = revmap_get_blkno (revmap , heapBlk );
338+ if (!BlockNumberIsValid (revmapBlk ))
339+ {
340+ /* revmap page doesn't exist: range not summarized, we're done */
341+ brinRevmapTerminate (revmap );
342+ return true;
343+ }
344+
345+ /* Lock the revmap page, obtain the index tuple pointer from it */
346+ revmapBuf = brinLockRevmapPageForUpdate (revmap , heapBlk );
347+ revmapPg = BufferGetPage (revmapBuf );
348+ revmapOffset = HEAPBLK_TO_REVMAP_INDEX (revmap -> rm_pagesPerRange , heapBlk );
349+
350+ contents = (RevmapContents * ) PageGetContents (revmapPg );
351+ iptr = contents -> rm_tids ;
352+ iptr += revmapOffset ;
353+
354+ if (!ItemPointerIsValid (iptr ))
355+ {
356+ /* no index tuple: range not summarized, we're done */
357+ LockBuffer (revmapBuf , BUFFER_LOCK_UNLOCK );
358+ brinRevmapTerminate (revmap );
359+ return true;
360+ }
361+
362+ regBuf = ReadBuffer (idxrel , ItemPointerGetBlockNumber (iptr ));
363+ LockBuffer (regBuf , BUFFER_LOCK_EXCLUSIVE );
364+ regPg = BufferGetPage (regBuf );
365+
366+ /* if this is no longer a regular page, tell caller to start over */
367+ if (!BRIN_IS_REGULAR_PAGE (regPg ))
368+ {
369+ LockBuffer (revmapBuf , BUFFER_LOCK_UNLOCK );
370+ LockBuffer (regBuf , BUFFER_LOCK_UNLOCK );
371+ brinRevmapTerminate (revmap );
372+ return false;
373+ }
374+
375+ regOffset = ItemPointerGetOffsetNumber (iptr );
376+ if (regOffset > PageGetMaxOffsetNumber (regPg ))
377+ ereport (ERROR ,
378+ (errcode (ERRCODE_INDEX_CORRUPTED ),
379+ errmsg ("corrupted BRIN index: inconsistent range map" )));
380+
381+ lp = PageGetItemId (regPg , regOffset );
382+ if (!ItemIdIsUsed (lp ))
383+ ereport (ERROR ,
384+ (errcode (ERRCODE_INDEX_CORRUPTED ),
385+ errmsg ("corrupted BRIN index: inconsistent range map" )));
386+ tup = (BrinTuple * ) PageGetItem (regPg , lp );
387+ /* XXX apply sanity checks? Might as well delete a bogus tuple ... */
388+
389+ /*
390+ * We're only removing data, not reading it, so there's no need to
391+ * TestForOldSnapshot here.
392+ */
393+
394+ /*
395+ * Because of SUE lock, this function shouldn't run concurrently with
396+ * summarization. Placeholder tuples can only exist as leftovers from
397+ * crashed summarization, so if we detect any, we complain but proceed.
398+ */
399+ if (BrinTupleIsPlaceholder (tup ))
400+ ereport (WARNING ,
401+ (errmsg ("leftover placeholder tuple detected in BRIN index \"%s\", deleting" ,
402+ RelationGetRelationName (idxrel ))));
403+
404+ START_CRIT_SECTION ();
405+
406+ ItemPointerSetInvalid (& invalidIptr );
407+ brinSetHeapBlockItemptr (revmapBuf , revmap -> rm_pagesPerRange , heapBlk ,
408+ invalidIptr );
409+ PageIndexTupleDeleteNoCompact (regPg , regOffset );
410+ /* XXX record free space in FSM? */
411+
412+ MarkBufferDirty (regBuf );
413+ MarkBufferDirty (revmapBuf );
414+
415+ if (RelationNeedsWAL (idxrel ))
416+ {
417+ xl_brin_desummarize xlrec ;
418+ XLogRecPtr recptr ;
419+
420+ xlrec .heapBlk = heapBlk ;
421+ xlrec .regOffset = regOffset ;
422+
423+ XLogBeginInsert ();
424+ XLogRegisterData ((char * ) & xlrec , SizeOfBrinDesummarize );
425+ XLogRegisterBuffer (0 , revmapBuf , 0 );
426+ XLogRegisterBuffer (1 , regBuf , REGBUF_STANDARD );
427+ recptr = XLogInsert (RM_BRIN_ID , XLOG_BRIN_DESUMMARIZE );
428+ PageSetLSN (revmapPg , recptr );
429+ PageSetLSN (regPg , recptr );
430+ }
431+
432+ END_CRIT_SECTION ();
433+
434+ UnlockReleaseBuffer (regBuf );
435+ LockBuffer (revmapBuf , BUFFER_LOCK_UNLOCK );
436+ brinRevmapTerminate (revmap );
437+
438+ return true;
439+ }
440+
307441/*
308442 * Given a heap block number, find the corresponding physical revmap block
309443 * number and return it. If the revmap page hasn't been allocated yet, return
0 commit comments