1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-30 21:42:05 +03:00

Use full 64-bit XID for checking if a deleted GiST page is old enough.

Otherwise, after a deleted page gets even older, it becomes unrecyclable
again. B-tree has the same problem, and has had since time immemorial,
but let's at least fix this in GiST, where this is new.

Backpatch to v12, where GiST page deletion was introduced.

Reviewed-by: Andrey Borodin
Discussion: https://www.postgresql.org/message-id/835A15A5-F1B4-4446-A711-BF48357EB602%40yandex-team.ru
This commit is contained in:
Heikki Linnakangas
2019-07-24 20:24:07 +03:00
parent e2e992c931
commit fb5344c969
9 changed files with 134 additions and 28 deletions

View File

@ -16,6 +16,7 @@
#ifndef GIST_H
#define GIST_H
#include "access/transam.h"
#include "access/xlog.h"
#include "access/xlogdefs.h"
#include "storage/block.h"
@ -140,8 +141,6 @@ typedef struct GISTENTRY
#define GIST_LEAF(entry) (GistPageIsLeaf((entry)->page))
#define GistPageIsDeleted(page) ( GistPageGetOpaque(page)->flags & F_DELETED)
#define GistPageSetDeleted(page) ( GistPageGetOpaque(page)->flags |= F_DELETED)
#define GistPageSetNonDeleted(page) ( GistPageGetOpaque(page)->flags &= ~F_DELETED)
#define GistTuplesDeleted(page) ( GistPageGetOpaque(page)->flags & F_TUPLES_DELETED)
#define GistMarkTuplesDeleted(page) ( GistPageGetOpaque(page)->flags |= F_TUPLES_DELETED)
@ -158,9 +157,45 @@ typedef struct GISTENTRY
#define GistPageGetNSN(page) ( PageXLogRecPtrGet(GistPageGetOpaque(page)->nsn))
#define GistPageSetNSN(page, val) ( PageXLogRecPtrSet(GistPageGetOpaque(page)->nsn, val))
/* For deleted pages we store last xid which could see the page in scan */
#define GistPageGetDeleteXid(page) ( ((PageHeader) (page))->pd_prune_xid )
#define GistPageSetDeleteXid(page, val) ( ((PageHeader) (page))->pd_prune_xid = val)
/*
* On a deleted page, we store this struct. A deleted page doesn't contain any
* tuples, so we don't use the normal page layout with line pointers. Instead,
* this struct is stored right after the standard page header. pd_lower points
* to the end of this struct. If we add fields to this struct in the future, we
* can distinguish the old and new formats by pd_lower.
*/
typedef struct GISTDeletedPageContents
{
/* last xid which could see the page in a scan */
FullTransactionId deleteXid;
} GISTDeletedPageContents;
static inline void
GistPageSetDeleted(Page page, FullTransactionId deletexid)
{
Assert(PageIsEmpty(page));
GistPageGetOpaque(page)->flags |= F_DELETED;
((PageHeader) page)->pd_lower = MAXALIGN(SizeOfPageHeaderData) + sizeof(GISTDeletedPageContents);
((GISTDeletedPageContents *) PageGetContents(page))->deleteXid = deletexid;
}
static inline FullTransactionId
GistPageGetDeleteXid(Page page)
{
Assert(GistPageIsDeleted(page));
/* Is the deleteXid field present? */
if (((PageHeader) page)->pd_lower >= MAXALIGN(SizeOfPageHeaderData) +
offsetof(GISTDeletedPageContents, deleteXid) + sizeof(FullTransactionId))
{
return ((GISTDeletedPageContents *) PageGetContents(page))->deleteXid;
}
else
return FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
}
/*
* Vector of GISTENTRY structs; user-defined methods union and picksplit