]> 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:38:32 +0000 (11:38 +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 26f3ddbf8534cb75379100f64df223ad2844264a..1cea03d73a6641c4f1a0a451fa6fcf9e9d506851 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 7d51070788c96c83b90e65c436beddf10b7d0372..d88f5ea0f81786464bd1f29a32261230250fa4aa 100644 (file)
@@ -677,3 +677,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 3691e17e4414901b7c7505c873ab9e86abbe1fb3..af5fef1ea243c19509bdfaf0d7611b195a40c281 100644 (file)
@@ -134,7 +134,7 @@ gistDeleteSubtree(GistVacuum *gv, BlockNumber blkno)
                PageSetTLI(page, ThisTimeLineID);
        }
        else
-               PageSetLSN(page, XLogRecPtrForTemp);
+               PageSetLSN(page, GetXLogRecPtrForTemp());
 
        END_CRIT_SECTION();
 
@@ -253,7 +253,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)
@@ -467,7 +467,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion)
                                pfree(rdata);
                        }
                        else
-                               PageSetLSN(page, XLogRecPtrForTemp);
+                               PageSetLSN(page, GetXLogRecPtrForTemp());
                }
 
                END_CRIT_SECTION();
@@ -791,7 +791,7 @@ gistbulkdelete(PG_FUNCTION_ARGS)
                                        pfree(rdata);
                                }
                                else
-                                       PageSetLSN(page, XLogRecPtrForTemp);
+                                       PageSetLSN(page, GetXLogRecPtrForTemp());
 
                                END_CRIT_SECTION();
                        }
index b231492d7a9bff5b8b630182e291b2fcc316a78f..3e5098a5051abae132468a280fdc689e4dca0e14 100644 (file)
@@ -94,7 +94,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
@@ -342,6 +341,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);