]> granicus.if.org Git - postgresql/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:29:28 +0000 (11:29 +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 2742969c6af8372a6e6afd84993dea57d00d932c..405934caad114bed342845adbed8f509cfd55d3a 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 78eb378725287cfd68daa59a30bd99b182d92231..a2e8fe0cfb9f1b61ee94d9e2c7b3d3ae6a565aac 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 975f9d8c56fe5533624bb1c31747d26457767243..0433b47ae1eb59d72796848c5a0d3b977c5de373 100644 (file)
@@ -138,7 +138,7 @@ gistDeleteSubtree(GistVacuum *gv, BlockNumber blkno)
                PageSetTLI(page, ThisTimeLineID);
        }
        else
-               PageSetLSN(page, XLogRecPtrForTemp);
+               PageSetLSN(page, GetXLogRecPtrForTemp());
 
        END_CRIT_SECTION();
 
@@ -245,7 +245,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)
@@ -460,7 +460,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion)
                                pfree(rdata);
                        }
                        else
-                               PageSetLSN(page, XLogRecPtrForTemp);
+                               PageSetLSN(page, GetXLogRecPtrForTemp());
                }
 
                END_CRIT_SECTION();
@@ -774,7 +774,7 @@ gistbulkdelete(PG_FUNCTION_ARGS)
                                        pfree(rdata);
                                }
                                else
-                                       PageSetLSN(page, XLogRecPtrForTemp);
+                                       PageSetLSN(page, GetXLogRecPtrForTemp());
 
                                END_CRIT_SECTION();
                        }
index 2ce46542af081656a64bdbd9bafc051aa311e06a..e375326491030d85abd4a922566bd9e416d3367a 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);