Fix double-XLogBeginInsert call in GIN page splits.
authorHeikki Linnakangas <[email protected]>
Sun, 28 Jun 2015 18:59:29 +0000 (21:59 +0300)
committerHeikki Linnakangas <[email protected]>
Sun, 28 Jun 2015 19:16:21 +0000 (22:16 +0300)
If data checksums or wal_log_hints is on, and a GIN page is split, the code
to find a new, empty, block was called after having already called
XLogBeginInsert(). That causes an assertion failure or PANIC, if finding the
new block involves updating a FSM page that had not been modified since last
checkpoint, because that update is WAL-logged, which calls XLogBeginInsert
again. Nested XLogBeginInsert calls are not supported.

To fix, rearrange GIN code so that XLogBeginInsert is called later, after
finding the victim buffers.

Reported by Jeff Janes.

src/backend/access/gin/ginbtree.c
src/backend/access/gin/gindatapage.c
src/backend/access/gin/ginentrypage.c

index 3f92c56d3c0fe7a0c9616741b6d8c447e1c2979c..f0ff91aba2fcfce51f57e2d4c67f5ed092f0168d 100644 (file)
@@ -358,20 +358,15 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
     * placeToPage can register some data to the WAL record.
     *
     * If placeToPage returns INSERTED, placeToPage has already called
-    * START_CRIT_SECTION(), and we're responsible for calling
-    * END_CRIT_SECTION. When it returns INSERTED, it is also responsible for
-    * registering any data required to replay the operation with
-    * XLogRegisterData(0, ...). It may only add data to block index 0; the
-    * main data of the WAL record is reserved for this function.
+    * START_CRIT_SECTION() and XLogBeginInsert(), and registered any data
+    * required to replay the operation, in block index 0. We're responsible
+    * for filling in the main data portion of the WAL record, calling
+    * XLogInsert(), and END_CRIT_SECTION.
     *
     * If placeToPage returns SPLIT, we're wholly responsible for WAL logging.
     * Splits happen infrequently, so we just make a full-page image of all
     * the pages involved.
     */
-
-   if (RelationNeedsWAL(btree->index))
-       XLogBeginInsert();
-
    rc = btree->placeToPage(btree, stack->buffer, stack,
                            insertdata, updateblkno,
                            &newlpage, &newrpage);
@@ -558,6 +553,8 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
        {
            XLogRecPtr  recptr;
 
+           XLogBeginInsert();
+
            /*
             * We just take full page images of all the split pages. Splits
             * are uncommon enough that it's not worth complicating the code
index 8e81f6c8687b9d313136ea85229de76e335d3ea5..ec8c94bcbd10a5354737aefc1f3bcaaf3b804e9d 100644 (file)
@@ -600,7 +600,10 @@ dataPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
         */
        MemoryContextSwitchTo(oldCxt);
        if (RelationNeedsWAL(btree->index))
+       {
+           XLogBeginInsert();
            registerLeafRecompressWALData(buf, leaf);
+       }
        START_CRIT_SECTION();
        dataPlaceToPageLeafRecompress(buf, leaf);
 
@@ -1120,6 +1123,7 @@ dataPlaceToPageInternal(GinBtree btree, Buffer buf, GinBtreeStack *stack,
        data.offset = off;
        data.newitem = *pitem;
 
+       XLogBeginInsert();
        XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
        XLogRegisterBufData(0, (char *) &data,
                            sizeof(ginxlogInsertDataInternal));
index 9997eae1bcdedff4cdff1db12e4e2f520ca00281..c912e60a11283a6d8032623bd8f1122a724c8c90 100644 (file)
@@ -557,6 +557,7 @@ entryPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
        data.isDelete = insertData->isDelete;
        data.offset = off;
 
+       XLogBeginInsert();
        XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
        XLogRegisterBufData(0, (char *) &data,
                            offsetof(ginxlogInsertEntry, tuple));