Skip to content

Commit 579b9f9

Browse files
committed
Fix handling of all-zero pages in SP-GiST vacuum.
SP-GiST initialized an all-zeros page at vacuum, but that was not WAL-logged, which is not safe. You might get a torn page write, when it gets flushed to disk, and end-up with a half-initialized index page. To fix, leave it in the all-zeros state, and add it to the FSM. It will be initialized when reused. Also don't set the page-deleted flag when recycling an empty page. That was also not WAL-logged, and a torn write of that would cause the page to have an invalid checksum. Backpatch to 9.2, where SP-GiST indexes were added.
1 parent 491c24f commit 579b9f9

File tree

2 files changed

+10
-21
lines changed

2 files changed

+10
-21
lines changed

src/backend/access/spgist/spgvacuum.c

+8-19
Original file line numberDiff line numberDiff line change
@@ -618,14 +618,10 @@ spgvacuumpage(spgBulkDeleteState *bds, BlockNumber blkno)
618618
{
619619
/*
620620
* We found an all-zero page, which could happen if the database
621-
* crashed just after extending the file. Initialize and recycle it.
621+
* crashed just after extending the file. Recycle it.
622622
*/
623-
SpGistInitBuffer(buffer, 0);
624-
SpGistPageSetDeleted(page);
625-
/* We don't bother to WAL-log this action; easy to redo */
626-
MarkBufferDirty(buffer);
627623
}
628-
else if (SpGistPageIsDeleted(page))
624+
else if (PageIsEmpty(page))
629625
{
630626
/* nothing to do */
631627
}
@@ -651,30 +647,23 @@ spgvacuumpage(spgBulkDeleteState *bds, BlockNumber blkno)
651647
/*
652648
* The root pages must never be deleted, nor marked as available in FSM,
653649
* because we don't want them ever returned by a search for a place to put
654-
* a new tuple. Otherwise, check for empty/deletable page, and make sure
655-
* FSM knows about it.
650+
* a new tuple. Otherwise, check for empty page, and make sure the FSM
651+
* knows about it.
656652
*/
657653
if (!SpGistBlockIsRoot(blkno))
658654
{
659-
/* If page is now empty, mark it deleted */
660-
if (PageIsEmpty(page) && !SpGistPageIsDeleted(page))
661-
{
662-
SpGistPageSetDeleted(page);
663-
/* We don't bother to WAL-log this action; easy to redo */
664-
MarkBufferDirty(buffer);
665-
}
666-
667-
if (SpGistPageIsDeleted(page))
655+
if (PageIsEmpty(page))
668656
{
669657
RecordFreeIndexPage(index, blkno);
670658
bds->stats->pages_deleted++;
671659
}
672660
else
661+
{
662+
SpGistSetLastUsedPage(index, buffer);
673663
bds->lastFilledBlock = blkno;
664+
}
674665
}
675666

676-
SpGistSetLastUsedPage(index, buffer);
677-
678667
UnlockReleaseBuffer(buffer);
679668
}
680669

src/include/access/spgist_private.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ typedef SpGistPageOpaqueData *SpGistPageOpaque;
4848

4949
/* Flag bits in page special space */
5050
#define SPGIST_META (1<<0)
51-
#define SPGIST_DELETED (1<<1)
51+
#define SPGIST_DELETED (1<<1) /* never set, but keep for backwards
52+
* compatibility */
5253
#define SPGIST_LEAF (1<<2)
5354
#define SPGIST_NULLS (1<<3)
5455

5556
#define SpGistPageGetOpaque(page) ((SpGistPageOpaque) PageGetSpecialPointer(page))
5657
#define SpGistPageIsMeta(page) (SpGistPageGetOpaque(page)->flags & SPGIST_META)
5758
#define SpGistPageIsDeleted(page) (SpGistPageGetOpaque(page)->flags & SPGIST_DELETED)
58-
#define SpGistPageSetDeleted(page) (SpGistPageGetOpaque(page)->flags |= SPGIST_DELETED)
5959
#define SpGistPageIsLeaf(page) (SpGistPageGetOpaque(page)->flags & SPGIST_LEAF)
6060
#define SpGistPageStoresNulls(page) (SpGistPageGetOpaque(page)->flags & SPGIST_NULLS)
6161

0 commit comments

Comments
 (0)