BRIN: Handle concurrent desummarization properly
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 12 Aug 2020 19:33:36 +0000 (15:33 -0400)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 12 Aug 2020 19:33:36 +0000 (15:33 -0400)
If a page range is desummarized at just the right time concurrently with
an index walk, BRIN would raise an error indicating index corruption.
This is scary and unhelpful; silently returning that the page range is
not summarized is sufficient reaction.

This bug was introduced by commit 975ad4e602ff as additional protection
against a bug whose actual fix was elsewhere.  Backpatch equally.

Reported-By: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
Diagnosed-By: Alexander Lakhin <exclusion@gmail.com>
Discussion: https://postgr.es/m/2588667e-d07d-7e10-74e2-7e1e46194491@postgrespro.ru
Backpatch: 9.5 - master

src/backend/access/brin/brin_revmap.c

index e8b8308f82ec8b47e08768491b623846523063ed..35746714a7c4f09da340ef795d3fb027bbc70184 100644 (file)
@@ -282,10 +282,17 @@ brinGetTupleForHeapBlock(BrinRevmap *revmap, BlockNumber heapBlk,
                /* If we land on a revmap page, start over */
                if (BRIN_IS_REGULAR_PAGE(page))
                {
+                       /*
+                        * If the offset number is greater than what's in the page, it's
+                        * possible that the range was desummarized concurrently. Just
+                        * return NULL to handle that case.
+                        */
                        if (*off > PageGetMaxOffsetNumber(page))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_INDEX_CORRUPTED),
-                                                errmsg_internal("corrupted BRIN index: inconsistent range map")));
+                       {
+                               LockBuffer(*buf, BUFFER_LOCK_UNLOCK);
+                               return NULL;
+                       }
+
                        lp = PageGetItemId(page, *off);
                        if (ItemIdIsUsed(lp))
                        {