Invalidate binary search bounds consistently.
authorPeter Geoghegan <pg@bowt.ie>
Thu, 4 Apr 2019 16:38:08 +0000 (09:38 -0700)
committerPeter Geoghegan <pg@bowt.ie>
Thu, 4 Apr 2019 16:38:08 +0000 (09:38 -0700)
_bt_check_unique() failed to invalidate binary search bounds in the
event of a live conflict following commit e5adcb78.  This resulted in
problems after waiting for the conflicting xact to commit or abort.  The
subsequent call to _bt_check_unique() would restore the initial binary
search bounds, rather than starting a new search.  Fix by explicitly
invalidating bounds when it becomes clear that there is a live conflict
that insertion will have to wait to resolve.

Ashutosh Sharma, with a few additional tweaks by me.

Author: Ashutosh Sharma
Reported-By: Ashutosh Sharma
Diagnosed-By: Ashutosh Sharma
Discussion: https://postgr.es/m/CAE9k0PnQp-qr-UYKMSCzdC2FBzdE4wKP41hZrZvvP26dKLonLg@mail.gmail.com

src/backend/access/nbtree/nbtinsert.c

index 96b7593fc1c22b8d69db10a60f6d74d7bd6c26d2..4fda0efe9ab400b68c8a2a48f8994328d8633210 100644 (file)
@@ -347,6 +347,7 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
         * This also saves the binary search bounds in insertstate.  We use them
         * in the fastpath below, but also in the _bt_findinsertloc() call later.
         */
+       Assert(!insertstate->bounds_valid);
        offset = _bt_binsrch_insert(rel, insertstate);
 
        /*
@@ -447,7 +448,8 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
                                         * check, then don't bother checking if the tuple is being
                                         * updated in another transaction. Just return the fact
                                         * that it is a potential conflict and leave the full
-                                        * check till later.
+                                        * check till later. Don't invalidate binary search
+                                        * bounds.
                                         */
                                        if (checkUnique == UNIQUE_CHECK_PARTIAL)
                                        {
@@ -470,6 +472,8 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
                                                        _bt_relbuf(rel, nbuf);
                                                /* Tell _bt_doinsert to wait... */
                                                *speculativeToken = SnapshotDirty.speculativeToken;
+                                               /* Caller releases lock on buf immediately */
+                                               insertstate->bounds_valid = false;
                                                return xwait;
                                        }
 
@@ -526,6 +530,7 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
                                                _bt_relbuf(rel, nbuf);
                                        _bt_relbuf(rel, insertstate->buf);
                                        insertstate->buf = InvalidBuffer;
+                                       insertstate->bounds_valid = false;
 
                                        {
                                                Datum           values[INDEX_MAX_KEYS];
@@ -601,6 +606,7 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
                        }
                        maxoff = PageGetMaxOffsetNumber(page);
                        offset = P_FIRSTDATAKEY(opaque);
+                       /* Don't invalidate binary search bounds */
                }
        }