]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
The GiST scan algorithm uses LSNs to detect concurrent pages splits, but
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 16 Nov 2010 09:02:11 +0000 (11:02 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 16 Nov 2010 09:25:43 +0000 (11:25 +0200)
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
src/backend/access/gist/gistutil.c
src/backend/access/gist/gistvacuum.c
src/include/access/gist_private.h

index 987d8c702a8994540c774b985917b749c23977c2..0ff334daf84d22bb7eb7fac0c340ea09d504ed6d 100644 (file)
@@ -20,8 +20,6 @@
 #include "miscadmin.h"
 #include "utils/memutils.h"
 
-const XLogRecPtr XLogRecPtrForTemp = {1, 1};
-
 /* Working state for gistbuild and its callback */
 typedef struct
 {
@@ -130,7 +128,7 @@ gistbuild(PG_FUNCTION_ARGS)
                PageSetTLI(page, ThisTimeLineID);
        }
        else
-               PageSetLSN(page, XLogRecPtrForTemp);
+               PageSetLSN(page, GetXLogRecPtrForTemp());
 
        UnlockReleaseBuffer(buffer);
 
@@ -421,7 +419,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
                {
                        for (ptr = dist; ptr; ptr = ptr->next)
                        {
-                               PageSetLSN(ptr->page, XLogRecPtrForTemp);
+                               PageSetLSN(ptr->page, GetXLogRecPtrForTemp());
                        }
                }
 
@@ -489,7 +487,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
                        PageSetTLI(state->stack->page, ThisTimeLineID);
                }
                else
-                       PageSetLSN(state->stack->page, XLogRecPtrForTemp);
+                       PageSetLSN(state->stack->page, GetXLogRecPtrForTemp());
 
                if (state->stack->blkno == GIST_ROOT_BLKNO)
                        state->needInsertComplete = false;
@@ -1025,7 +1023,7 @@ gistnewroot(Relation r, Buffer buffer, IndexTuple *itup, int len, ItemPointer ke
                PageSetTLI(page, ThisTimeLineID);
        }
        else
-               PageSetLSN(page, XLogRecPtrForTemp);
+               PageSetLSN(page, GetXLogRecPtrForTemp());
 
        END_CRIT_SECTION();
 }
index ff22bd2b6524708ba114f163e7098f20c7b23a41..fda7c5df2ee0c721e71114b655b7c251c8d6211c 100644 (file)
@@ -676,3 +676,24 @@ gistoptions(PG_FUNCTION_ARGS)
                PG_RETURN_BYTEA_P(result);
        PG_RETURN_NULL();
 }
+
+/*
+ * 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;
+}
index 5f5060280ddc51d68c9c81d535673ac8bdd7fea8..b941e0a4a084d00e73979d223ac3b1beee1623b5 100644 (file)
@@ -133,7 +133,7 @@ gistDeleteSubtree(GistVacuum *gv, BlockNumber blkno)
                PageSetTLI(page, ThisTimeLineID);
        }
        else
-               PageSetLSN(page, XLogRecPtrForTemp);
+               PageSetLSN(page, GetXLogRecPtrForTemp());
 
        END_CRIT_SECTION();
 
@@ -252,7 +252,7 @@ vacuumSplitPage(GistVacuum *gv, Page tempPage, Buffer buffer, IndexTuple *addon,
        else
        {
                for (ptr = dist; ptr; ptr = ptr->next)
-                       PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp);
+                       PageSetLSN(BufferGetPage(ptr->buffer), GetXLogRecPtrForTemp());
        }
 
        for (ptr = dist; ptr; ptr = ptr->next)
@@ -466,7 +466,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion)
                                pfree(rdata);
                        }
                        else
-                               PageSetLSN(page, XLogRecPtrForTemp);
+                               PageSetLSN(page, GetXLogRecPtrForTemp());
                }
 
                END_CRIT_SECTION();
@@ -789,7 +789,7 @@ gistbulkdelete(PG_FUNCTION_ARGS)
                                        pfree(rdata);
                                }
                                else
-                                       PageSetLSN(page, XLogRecPtrForTemp);
+                                       PageSetLSN(page, GetXLogRecPtrForTemp());
 
                                END_CRIT_SECTION();
                        }
index cab33bc9b6058daf73705db0f0b22380d36b2c83..271f57505785bd712ca36ff1948d319edc2c2185 100644 (file)
@@ -91,7 +91,6 @@ typedef struct GISTScanOpaqueData
 typedef GISTScanOpaqueData *GISTScanOpaque;
 
 /* XLog stuff */
-extern const XLogRecPtr XLogRecPtrForTemp;
 
 #define XLOG_GIST_PAGE_UPDATE          0x00
 #define XLOG_GIST_NEW_ROOT                     0x20
@@ -341,6 +340,8 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
                                 GISTENTRY *entry2, bool isnull2,
                                 Datum *dst, bool *dstisnull);
 
+extern XLogRecPtr GetXLogRecPtrForTemp(void);
+
 /* gistvacuum.c */
 extern Datum gistbulkdelete(PG_FUNCTION_ARGS);
 extern Datum gistvacuumcleanup(PG_FUNCTION_ARGS);