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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy