From acbffaa77d0c4846f8749daa45fde507be1e969b Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Tue, 16 Nov 2010 11:02:11 +0200 Subject: [PATCH] The GiST scan algorithm uses LSNs to detect concurrent pages splits, but temporary indexes are not WAL-logged. We used a constant LSN for temporary indexes, on the assumption that we don't need to worry about concurrent page splits in temporary indexes because they're only visible to the current session. But that assumption is wrong, it's possible to insert rows and split pages in the same session, while a scan is in progress. For example, by opening a cursor and fetching some rows, and INSERTing new rows before fetching some more. Fix by generating fake increasing LSNs, used in place of real LSNs in temporary GiST indexes. --- src/backend/access/gist/gist.c | 10 ++++------ src/backend/access/gist/gistutil.c | 21 +++++++++++++++++++++ src/backend/access/gist/gistvacuum.c | 6 +++--- src/include/access/gist_private.h | 3 ++- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 00b1620ab00..fbc828e7832 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -23,8 +23,6 @@ #include "miscadmin.h" #include "utils/memutils.h" -const XLogRecPtr XLogRecPtrForTemp = {1, 1}; - /* Working state for gistbuild and its callback */ typedef struct { @@ -127,7 +125,7 @@ gistbuild(PG_FUNCTION_ARGS) END_CRIT_SECTION(); } else - PageSetLSN(BufferGetPage(buffer), XLogRecPtrForTemp); + PageSetLSN(BufferGetPage(buffer), GetXLogRecPtrForTemp()); LockBuffer(buffer, GIST_UNLOCK); WriteBuffer(buffer); @@ -356,7 +354,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate) ptr = dist; while (ptr) { - PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp); + PageSetLSN(BufferGetPage(ptr->buffer), GetXLogRecPtrForTemp()); ptr = ptr->next; } } @@ -475,7 +473,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate) END_CRIT_SECTION(); } else - PageSetLSN(state->stack->page, XLogRecPtrForTemp); + PageSetLSN(state->stack->page, GetXLogRecPtrForTemp()); if (state->stack->blkno == GIST_ROOT_BLKNO) state->needInsertComplete = false; @@ -1206,7 +1204,7 @@ gistnewroot(Relation r, Buffer buffer, IndexTuple *itup, int len, ItemPointer ke END_CRIT_SECTION(); } else - PageSetLSN(page, XLogRecPtrForTemp); + PageSetLSN(page, GetXLogRecPtrForTemp()); } void diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index c0d904c35a4..9f43b4520c9 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -977,3 +977,24 @@ gistNewBuffer(Relation r) return buffer; } + +/* + * Temporary GiST indexes are not WAL-logged, but we need LSNs to detect + * concurrent page splits anyway. GetXLogRecPtrForTemp() provides a fake + * sequence of LSNs for that purpose. Each call generates an LSN that is + * greater than any previous value returned by this function in the same + * session. + */ +XLogRecPtr +GetXLogRecPtrForTemp(void) +{ + static XLogRecPtr counter = {0, 1}; + + counter.xrecoff++; + if (counter.xrecoff == 0) + { + counter.xlogid++; + counter.xrecoff++; + } + return counter; +} diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index 9d02ea7b46f..fa545f74683 100644 --- a/src/backend/access/gist/gistvacuum.c +++ b/src/backend/access/gist/gistvacuum.c @@ -201,7 +201,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion) ptr = dist; while (ptr) { - PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp); + PageSetLSN(BufferGetPage(ptr->buffer), GetXLogRecPtrForTemp()); ptr = ptr->next; } } @@ -306,7 +306,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion) pfree(rdata); } else - PageSetLSN(page, XLogRecPtrForTemp); + PageSetLSN(page, GetXLogRecPtrForTemp()); WriteBuffer(buffer); } else @@ -589,7 +589,7 @@ gistbulkdelete(PG_FUNCTION_ARGS) pfree(rdata); } else - PageSetLSN(page, XLogRecPtrForTemp); + PageSetLSN(page, GetXLogRecPtrForTemp()); WriteNoReleaseBuffer(buffer); } } diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 1668f80a9c9..18c766c7891 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -91,7 +91,6 @@ typedef struct GISTScanOpaqueData typedef GISTScanOpaqueData *GISTScanOpaque; /* XLog stuff */ -extern const XLogRecPtr XLogRecPtrForTemp; #define XLOG_GIST_ENTRY_UPDATE 0x00 #define XLOG_GIST_ENTRY_DELETE 0x10 @@ -318,6 +317,8 @@ extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, void gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v, IndexTuple *itup, int len, GISTSTATE *giststate); +extern XLogRecPtr GetXLogRecPtrForTemp(void); + /* gistvacuum.c */ extern Datum gistbulkdelete(PG_FUNCTION_ARGS); extern Datum gistvacuumcleanup(PG_FUNCTION_ARGS);