The GiST scan algorithm uses LSNs to detect concurrent pages splits, but
authorHeikki Linnakangas <[email protected]>
Tue, 16 Nov 2010 09:02:11 +0000 (11:02 +0200)
committerHeikki Linnakangas <[email protected]>
Tue, 16 Nov 2010 09:32:21 +0000 (11:32 +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 3054f98c9e81ed4e20bf05a03b92ceb867f6fed4..8c2dbc940f45a890a1a0a9e432d278b001200827 100644 (file)
@@ -22,8 +22,6 @@
 #include "storage/indexfsm.h"
 #include "utils/memutils.h"
 
-const XLogRecPtr XLogRecPtrForTemp = {1, 1};
-
 /* Working state for gistbuild and its callback */
 typedef struct
 {
@@ -132,7 +130,7 @@ gistbuild(PG_FUNCTION_ARGS)
        PageSetTLI(page, ThisTimeLineID);
    }
    else
-       PageSetLSN(page, XLogRecPtrForTemp);
+       PageSetLSN(page, GetXLogRecPtrForTemp());
 
    UnlockReleaseBuffer(buffer);
 
@@ -423,7 +421,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
        {
            for (ptr = dist; ptr; ptr = ptr->next)
            {
-               PageSetLSN(ptr->page, XLogRecPtrForTemp);
+               PageSetLSN(ptr->page, GetXLogRecPtrForTemp());
            }
        }
 
@@ -491,7 +489,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;
@@ -1027,7 +1025,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 28ab575425e54d5f8a5285c1fc464e568e4412e0..d488bbb42007c3d65be7d07571ce1b09cf98e517 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 0ff5ba840eeeff210085e9ce0ad33cde1b175299..dbe9406ceb8b6bc53d6bf03faa916571a6ca5e6e 100644 (file)
@@ -268,7 +268,7 @@ gistbulkdelete(PG_FUNCTION_ARGS)
                    pfree(rdata);
                }
                else
-                   PageSetLSN(page, XLogRecPtrForTemp);
+                   PageSetLSN(page, GetXLogRecPtrForTemp());
 
                END_CRIT_SECTION();
            }
index 34cc5d5a8bfdc0183028d14a31028a859f0dd4e4..f2dcbfb39b2b7982175ba3316f8c8ac496b67850 100644 (file)
@@ -87,7 +87,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
@@ -326,6 +325,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);