Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit f9b7fc6

Browse files
committed
Fix race in SSI interaction with empty btrees.
When predicate-locking btrees, we have a special case for completely empty btrees, since there is no page to lock. This was racy, because, without buffer lock held, a matching key could be inserted between the _bt_search() and the PredicateLockRelation() calls. Fix, by rechecking _bt_search() after taking the relation-level SIREAD lock, if using SERIALIZABLE isolation and an empty btree is discovered. Back-patch to all supported releases. Fixes one aspect of bug #17949. Reported-by: Artem Anisimov <artem.anisimov.255@gmail.com> Reviewed-by: Dmitry Dolgov <9erthalion6@gmail.com> Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi> Discussion: https://postgr.es/m/17949-a0f17035294a55e2%40postgresql.org
1 parent 562bee0 commit f9b7fc6

File tree

1 file changed

+26
-13
lines changed

1 file changed

+26
-13
lines changed

src/backend/access/nbtree/nbtsearch.c

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "access/nbtree.h"
1919
#include "access/relscan.h"
20+
#include "access/xact.h"
2021
#include "miscadmin.h"
2122
#include "pgstat.h"
2223
#include "storage/predicate.h"
@@ -1382,22 +1383,34 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
13821383
{
13831384
/*
13841385
* We only get here if the index is completely empty. Lock relation
1385-
* because nothing finer to lock exists.
1386+
* because nothing finer to lock exists. Without a buffer lock, it's
1387+
* possible for another transaction to insert data between
1388+
* _bt_search() and PredicateLockRelation(). We have to try again
1389+
* after taking the relation-level predicate lock, to close a narrow
1390+
* window where we wouldn't scan concurrently inserted tuples, but the
1391+
* writer wouldn't see our predicate lock.
13861392
*/
1387-
PredicateLockRelation(rel, scan->xs_snapshot);
1388-
1389-
/*
1390-
* mark parallel scan as done, so that all the workers can finish
1391-
* their scan
1392-
*/
1393-
_bt_parallel_done(scan);
1394-
BTScanPosInvalidate(so->currPos);
1393+
if (IsolationIsSerializable())
1394+
{
1395+
PredicateLockRelation(rel, scan->xs_snapshot);
1396+
stack = _bt_search(rel, NULL, &inskey, &buf, BT_READ,
1397+
scan->xs_snapshot);
1398+
_bt_freestack(stack);
1399+
}
13951400

1396-
return false;
1401+
if (!BufferIsValid(buf))
1402+
{
1403+
/*
1404+
* Mark parallel scan as done, so that all the workers can finish
1405+
* their scan.
1406+
*/
1407+
_bt_parallel_done(scan);
1408+
BTScanPosInvalidate(so->currPos);
1409+
return false;
1410+
}
13971411
}
1398-
else
1399-
PredicateLockPage(rel, BufferGetBlockNumber(buf),
1400-
scan->xs_snapshot);
1412+
1413+
PredicateLockPage(rel, BufferGetBlockNumber(buf), scan->xs_snapshot);
14011414

14021415
_bt_initialize_more_data(so, dir);
14031416

0 commit comments

Comments
 (0)