aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execMain.c
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2014-08-27 19:15:18 -0400
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2014-08-27 19:15:18 -0400
commit1c9701cfe58267cf5d79543a42ee4f0967cc73ab (patch)
tree8103133b312bbe48b0aecad9a3a4fb96184fb822 /src/backend/executor/execMain.c
parent9a2d94892f6a793d52ebabb8283b98a6e3ede3d6 (diff)
downloadpostgresql-1c9701cfe58267cf5d79543a42ee4f0967cc73ab.tar.gz
postgresql-1c9701cfe58267cf5d79543a42ee4f0967cc73ab.zip
Fix FOR UPDATE NOWAIT on updated tuple chains
If SELECT FOR UPDATE NOWAIT tries to lock a tuple that is concurrently being updated, it might fail to honor its NOWAIT specification and block instead of raising an error. Fix by adding a no-wait flag to EvalPlanQualFetch which it can pass down to heap_lock_tuple; also use it in EvalPlanQualFetch itself to avoid blocking while waiting for a concurrent transaction. Authors: Craig Ringer and Thomas Munro, tweaked by Álvaro http://www.postgresql.org/message-id/51FB6703.9090801@2ndquadrant.com Per Thomas Munro in the course of his SKIP LOCKED feature submission, who also provided one of the isolation test specs. Backpatch to 9.4, because that's as far back as it applies without conflicts (although the bug goes all the way back). To that branch also backpatch Thomas Munro's new NOWAIT test cases, committed in master by Heikki as commit 9ee16b49f0aac819bd4823d9b94485ef608b34e8 .
Diffstat (limited to 'src/backend/executor/execMain.c')
-rw-r--r--src/backend/executor/execMain.c24
1 files changed, 17 insertions, 7 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 072c7df0ada..01eda70f054 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1863,7 +1863,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
/*
* Get and lock the updated version of the row; if fail, return NULL.
*/
- copyTuple = EvalPlanQualFetch(estate, relation, lockmode,
+ copyTuple = EvalPlanQualFetch(estate, relation, lockmode, false /* wait */,
tid, priorXmax);
if (copyTuple == NULL)
@@ -1922,6 +1922,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* estate - executor state data
* relation - table containing tuple
* lockmode - requested tuple lock mode
+ * noWait - wait mode to pass to heap_lock_tuple
* *tid - t_ctid from the outdated tuple (ie, next updated version)
* priorXmax - t_xmax from the outdated tuple
*
@@ -1934,7 +1935,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* but we use "int" to avoid having to include heapam.h in executor.h.
*/
HeapTuple
-EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
+EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, bool noWait,
ItemPointer tid, TransactionId priorXmax)
{
HeapTuple copyTuple = NULL;
@@ -1978,14 +1979,23 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* If tuple is being updated by other transaction then we have to
- * wait for its commit/abort.
+ * wait for its commit/abort, or die trying.
*/
if (TransactionIdIsValid(SnapshotDirty.xmax))
{
ReleaseBuffer(buffer);
- XactLockTableWait(SnapshotDirty.xmax,
- relation, &tuple.t_data->t_ctid,
- XLTW_FetchUpdated);
+ if (noWait)
+ {
+ if (!ConditionalXactLockTableWait(SnapshotDirty.xmax))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ }
+ else
+ XactLockTableWait(SnapshotDirty.xmax,
+ relation, &tuple.t_data->t_ctid,
+ XLTW_FetchUpdated);
continue; /* loop back to repeat heap_fetch */
}
@@ -2012,7 +2022,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
*/
test = heap_lock_tuple(relation, &tuple,
estate->es_output_cid,
- lockmode, false /* wait */ ,
+ lockmode, noWait,
false, &buffer, &hufd);
/* We now have two pins on the buffer, get rid of one */
ReleaseBuffer(buffer);