Avoid testing tuple visibility without buffer lock.
authorTom Lane
Sun, 23 Oct 2016 23:14:32 +0000 (19:14 -0400)
committerTom Lane
Sun, 23 Oct 2016 23:14:32 +0000 (19:14 -0400)
INSERT ... ON CONFLICT (specifically ExecCheckHeapTupleVisible) contains
another example of this unsafe coding practice.  It is much harder to get
a failure out of it than the case fixed in commit 6292c2339, because in
most scenarios any hint bits that could be set would have already been set
earlier in the command.  However, Konstantin Knizhnik reported a failure
with a custom transaction manager, and it's clearly possible to get a
failure via a race condition in async-commit mode.

For lack of a reproducible example, no regression test case in this
commit.

I did some testing with Asserts added to tqual.c's functions, and can say
that running "make check-world" exposed these two bugs and no others.
The Asserts are messy enough that I've not added them to the code for now.

Report: <57EE93C8.8080504@postgrespro.ru>
Related-Discussion: 

src/backend/executor/nodeModifyTable.c

index 9d10467303ce8b891c8d8a8f3b484dce9daf70b3..05b6e3337f8065324333f9b3b1288a54248f623b 100644 (file)
@@ -177,6 +177,11 @@ ExecCheckHeapTupleVisible(EState *estate,
    if (!IsolationUsesXactSnapshot())
        return;
 
+   /*
+    * We need buffer pin and lock to call HeapTupleSatisfiesVisibility.
+    * Caller should be holding pin, but not lock.
+    */
+   LockBuffer(buffer, BUFFER_LOCK_SHARE);
    if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
    {
        /*
@@ -190,6 +195,7 @@ ExecCheckHeapTupleVisible(EState *estate,
                    (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
             errmsg("could not serialize access due to concurrent update")));
    }
+   LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 }
 
 /*