Skip to content

Commit f068299

Browse files
committed
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.
1 parent 6a93cb6 commit f068299

File tree

4 files changed

+31
-11
lines changed

4 files changed

+31
-11
lines changed

src/backend/access/gist/gist.c

+4-6
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
#include "miscadmin.h"
2121
#include "utils/memutils.h"
2222

23-
const XLogRecPtr XLogRecPtrForTemp = {1, 1};
24-
2523
/* Working state for gistbuild and its callback */
2624
typedef struct
2725
{
@@ -130,7 +128,7 @@ gistbuild(PG_FUNCTION_ARGS)
130128
PageSetTLI(page, ThisTimeLineID);
131129
}
132130
else
133-
PageSetLSN(page, XLogRecPtrForTemp);
131+
PageSetLSN(page, GetXLogRecPtrForTemp());
134132

135133
UnlockReleaseBuffer(buffer);
136134

@@ -421,7 +419,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
421419
{
422420
for (ptr = dist; ptr; ptr = ptr->next)
423421
{
424-
PageSetLSN(ptr->page, XLogRecPtrForTemp);
422+
PageSetLSN(ptr->page, GetXLogRecPtrForTemp());
425423
}
426424
}
427425

@@ -489,7 +487,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
489487
PageSetTLI(state->stack->page, ThisTimeLineID);
490488
}
491489
else
492-
PageSetLSN(state->stack->page, XLogRecPtrForTemp);
490+
PageSetLSN(state->stack->page, GetXLogRecPtrForTemp());
493491

494492
if (state->stack->blkno == GIST_ROOT_BLKNO)
495493
state->needInsertComplete = false;
@@ -1025,7 +1023,7 @@ gistnewroot(Relation r, Buffer buffer, IndexTuple *itup, int len, ItemPointer ke
10251023
PageSetTLI(page, ThisTimeLineID);
10261024
}
10271025
else
1028-
PageSetLSN(page, XLogRecPtrForTemp);
1026+
PageSetLSN(page, GetXLogRecPtrForTemp());
10291027

10301028
END_CRIT_SECTION();
10311029
}

src/backend/access/gist/gistutil.c

+21
Original file line numberDiff line numberDiff line change
@@ -677,3 +677,24 @@ gistoptions(PG_FUNCTION_ARGS)
677677
PG_RETURN_BYTEA_P(result);
678678
PG_RETURN_NULL();
679679
}
680+
681+
/*
682+
* Temporary GiST indexes are not WAL-logged, but we need LSNs to detect
683+
* concurrent page splits anyway. GetXLogRecPtrForTemp() provides a fake
684+
* sequence of LSNs for that purpose. Each call generates an LSN that is
685+
* greater than any previous value returned by this function in the same
686+
* session.
687+
*/
688+
XLogRecPtr
689+
GetXLogRecPtrForTemp(void)
690+
{
691+
static XLogRecPtr counter = {0, 1};
692+
693+
counter.xrecoff++;
694+
if (counter.xrecoff == 0)
695+
{
696+
counter.xlogid++;
697+
counter.xrecoff++;
698+
}
699+
return counter;
700+
}

src/backend/access/gist/gistvacuum.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ gistDeleteSubtree(GistVacuum *gv, BlockNumber blkno)
134134
PageSetTLI(page, ThisTimeLineID);
135135
}
136136
else
137-
PageSetLSN(page, XLogRecPtrForTemp);
137+
PageSetLSN(page, GetXLogRecPtrForTemp());
138138

139139
END_CRIT_SECTION();
140140

@@ -253,7 +253,7 @@ vacuumSplitPage(GistVacuum *gv, Page tempPage, Buffer buffer, IndexTuple *addon,
253253
else
254254
{
255255
for (ptr = dist; ptr; ptr = ptr->next)
256-
PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp);
256+
PageSetLSN(BufferGetPage(ptr->buffer), GetXLogRecPtrForTemp());
257257
}
258258

259259
for (ptr = dist; ptr; ptr = ptr->next)
@@ -467,7 +467,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion)
467467
pfree(rdata);
468468
}
469469
else
470-
PageSetLSN(page, XLogRecPtrForTemp);
470+
PageSetLSN(page, GetXLogRecPtrForTemp());
471471
}
472472

473473
END_CRIT_SECTION();
@@ -791,7 +791,7 @@ gistbulkdelete(PG_FUNCTION_ARGS)
791791
pfree(rdata);
792792
}
793793
else
794-
PageSetLSN(page, XLogRecPtrForTemp);
794+
PageSetLSN(page, GetXLogRecPtrForTemp());
795795

796796
END_CRIT_SECTION();
797797
}

src/include/access/gist_private.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ typedef struct GISTScanOpaqueData
9494
typedef GISTScanOpaqueData *GISTScanOpaque;
9595

9696
/* XLog stuff */
97-
extern const XLogRecPtr XLogRecPtrForTemp;
9897

9998
#define XLOG_GIST_PAGE_UPDATE 0x00
10099
#define XLOG_GIST_NEW_ROOT 0x20
@@ -342,6 +341,8 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
342341
GISTENTRY *entry2, bool isnull2,
343342
Datum *dst, bool *dstisnull);
344343

344+
extern XLogRecPtr GetXLogRecPtrForTemp(void);
345+
345346
/* gistvacuum.c */
346347
extern Datum gistbulkdelete(PG_FUNCTION_ARGS);
347348
extern Datum gistvacuumcleanup(PG_FUNCTION_ARGS);

0 commit comments

Comments
 (0)