]> 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:17 +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 00b1620ab00f6d3847c4681650c62f9270c1dbbc..fbc828e783239932d2e14849ae4a2cf98e7eecc9 100644 (file)
@@ -23,8 +23,6 @@
 #include "miscadmin.h"
 #include "utils/memutils.h"
 
-const XLogRecPtr XLogRecPtrForTemp = {1, 1};
-
 /* Working state for gistbuild and its callback */
 typedef struct
 {
@@ -127,7 +125,7 @@ gistbuild(PG_FUNCTION_ARGS)
                END_CRIT_SECTION();
        }
        else
-               PageSetLSN(BufferGetPage(buffer), XLogRecPtrForTemp);
+               PageSetLSN(BufferGetPage(buffer), GetXLogRecPtrForTemp());
        LockBuffer(buffer, GIST_UNLOCK);
        WriteBuffer(buffer);
 
@@ -356,7 +354,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
                        ptr = dist;
                        while (ptr)
                        {
-                               PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp);
+                               PageSetLSN(BufferGetPage(ptr->buffer), GetXLogRecPtrForTemp());
                                ptr = ptr->next;
                        }
                }
@@ -475,7 +473,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
                        END_CRIT_SECTION();
                }
                else
-                       PageSetLSN(state->stack->page, XLogRecPtrForTemp);
+                       PageSetLSN(state->stack->page, GetXLogRecPtrForTemp());
 
                if (state->stack->blkno == GIST_ROOT_BLKNO)
                        state->needInsertComplete = false;
@@ -1206,7 +1204,7 @@ gistnewroot(Relation r, Buffer buffer, IndexTuple *itup, int len, ItemPointer ke
                END_CRIT_SECTION();
        }
        else
-               PageSetLSN(page, XLogRecPtrForTemp);
+               PageSetLSN(page, GetXLogRecPtrForTemp());
 }
 
 void
index c0d904c35a4a5fc4a66c7be590a96996d749b0d1..9f43b4520c9cd33ed9e168c246a6a7874b6bb741 100644 (file)
@@ -977,3 +977,24 @@ gistNewBuffer(Relation r)
 
        return buffer;
 }
+
+/*
+ * 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 9d02ea7b46fb89d3fcb8241420a1e0252360df7a..fa545f74683277349ebd8c02f472381c158b94ad 100644 (file)
@@ -201,7 +201,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion)
                                        ptr = dist;
                                        while (ptr)
                                        {
-                                               PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp);
+                                               PageSetLSN(BufferGetPage(ptr->buffer), GetXLogRecPtrForTemp());
                                                ptr = ptr->next;
                                        }
                                }
@@ -306,7 +306,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion)
                        pfree(rdata);
                }
                else
-                       PageSetLSN(page, XLogRecPtrForTemp);
+                       PageSetLSN(page, GetXLogRecPtrForTemp());
                WriteBuffer(buffer);
        }
        else
@@ -589,7 +589,7 @@ gistbulkdelete(PG_FUNCTION_ARGS)
                                        pfree(rdata);
                                }
                                else
-                                       PageSetLSN(page, XLogRecPtrForTemp);
+                                       PageSetLSN(page, GetXLogRecPtrForTemp());
                                WriteNoReleaseBuffer(buffer);
                        }
                }
index 1668f80a9c99c244fcc4a98c148c096ca2863bd4..18c766c7891214b30cefbc7a6463ed36b20c2fc6 100644 (file)
@@ -91,7 +91,6 @@ typedef struct GISTScanOpaqueData
 typedef GISTScanOpaqueData *GISTScanOpaque;
 
 /* XLog stuff */
-extern const XLogRecPtr XLogRecPtrForTemp;
 
 #define XLOG_GIST_ENTRY_UPDATE 0x00
 #define XLOG_GIST_ENTRY_DELETE 0x10
@@ -318,6 +317,8 @@ extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
 void gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v,
                                  IndexTuple *itup, int len, GISTSTATE *giststate);
 
+extern XLogRecPtr GetXLogRecPtrForTemp(void);
+
 /* gistvacuum.c */
 extern Datum gistbulkdelete(PG_FUNCTION_ARGS);
 extern Datum gistvacuumcleanup(PG_FUNCTION_ARGS);