]> granicus.if.org Git - postgresql/commitdiff
Fix crash in the new GiST insertion code, when an update splits the root page.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Sun, 9 Jan 2011 19:09:58 +0000 (21:09 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Sun, 9 Jan 2011 19:36:22 +0000 (21:36 +0200)
This bug was exercised by contrib/intarray/bench, as noted by Tom Lane.

src/backend/access/gist/gist.c

index 4bd1e43827af14f39e82fba542d3588f6bdcb76d..9529413e80e970078796564b8d9b413dbac16213 100644 (file)
@@ -741,22 +741,28 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
                                /*
                                 * Update the tuple.
                                 *
-                                * gistinserthere() might have to split the page to make the
-                                * updated tuple fit. It will adjust the stack so that after
-                                * the call, we'll be holding a lock on the page containing
-                                * the tuple, which might have moved right.
-                                *
-                                * Except if this causes a root split, gistinserthere()
-                                * returns 'true'. In that case, stack only holds the new
-                                * root, and the child page was released. Have to start
-                                * all over.
+                                * We still hold the lock after gistinserttuples(), but it
+                                * might have to split the page to make the updated tuple fit.
+                                * In that case the updated tuple might migrate to the other
+                                * half of the split, so we have to go back to the parent and
+                                * descend back to the half that's a better fit for the new
+                                * tuple.
                                 */
                                if (gistinserttuples(&state, stack, giststate, &newtup, 1,
                                                                         stack->childoffnum, InvalidBuffer))
                                {
-                                       UnlockReleaseBuffer(stack->buffer);
-                                       xlocked = false;
-                                       state.stack = stack = stack->parent;
+                                       /*
+                                        * If this was a root split, the root page continues to
+                                        * be the parent and the updated tuple went to one of the
+                                        * child pages, so we just need to retry from the root
+                                        * page.
+                                        */
+                                       if (stack->blkno != GIST_ROOT_BLKNO)
+                                       {
+                                               UnlockReleaseBuffer(stack->buffer);
+                                               xlocked = false;
+                                               state.stack = stack = stack->parent;
+                                       }
                                        continue;
                                }
                        }