aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/executor/execMain.c24
-rw-r--r--src/backend/executor/nodeLockRows.c2
-rw-r--r--src/include/executor/executor.h2
-rw-r--r--src/test/isolation/expected/nowait-4.out19
-rw-r--r--src/test/isolation/expected/nowait-4_1.out19
-rw-r--r--src/test/isolation/expected/nowait-5.out37
-rw-r--r--src/test/isolation/isolation_schedule8
-rw-r--r--src/test/isolation/specs/nowait-4.spec30
-rw-r--r--src/test/isolation/specs/nowait-5.spec57
9 files changed, 186 insertions, 12 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);
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 298d4b4d017..814b61efcba 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -170,7 +170,7 @@ lnext:
}
/* updated, so fetch and lock the updated version */
- copyTuple = EvalPlanQualFetch(estate, erm->relation, lockmode,
+ copyTuple = EvalPlanQualFetch(estate, erm->relation, lockmode, erm->noWait,
&hufd.ctid, hufd.xmax);
if (copyTuple == NULL)
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 239aff32088..02661350d94 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -199,7 +199,7 @@ extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, int lockmode,
ItemPointer tid, TransactionId priorXmax);
extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
- int lockmode, ItemPointer tid, TransactionId priorXmax);
+ int lockmode, bool noWait, ItemPointer tid, TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
Plan *subplan, List *auxrowmarks, int epqParam);
extern void EvalPlanQualSetPlan(EPQState *epqstate,
diff --git a/src/test/isolation/expected/nowait-4.out b/src/test/isolation/expected/nowait-4.out
new file mode 100644
index 00000000000..26f59bef946
--- /dev/null
+++ b/src/test/isolation/expected/nowait-4.out
@@ -0,0 +1,19 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s2a s1a s2b s2c s2d s2e s1b s2f
+step s2a: SELECT pg_advisory_lock(0);
+pg_advisory_lock
+
+
+step s1a: SELECT * FROM foo WHERE pg_advisory_lock(0) IS NOT NULL FOR UPDATE NOWAIT; <waiting ...>
+step s2b: UPDATE foo SET data = data;
+step s2c: BEGIN;
+step s2d: UPDATE foo SET data = data;
+step s2e: SELECT pg_advisory_unlock(0);
+pg_advisory_unlock
+
+t
+step s1a: <... completed>
+error in steps s2e s1a: ERROR: could not obtain lock on row in relation "foo"
+step s1b: COMMIT;
+step s2f: COMMIT;
diff --git a/src/test/isolation/expected/nowait-4_1.out b/src/test/isolation/expected/nowait-4_1.out
new file mode 100644
index 00000000000..959d51baae3
--- /dev/null
+++ b/src/test/isolation/expected/nowait-4_1.out
@@ -0,0 +1,19 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s2a s1a s2b s2c s2d s2e s1b s2f
+step s2a: SELECT pg_advisory_lock(0);
+pg_advisory_lock
+
+
+step s1a: SELECT * FROM foo WHERE pg_advisory_lock(0) IS NOT NULL FOR UPDATE NOWAIT; <waiting ...>
+step s2b: UPDATE foo SET data = data;
+step s2c: BEGIN;
+step s2d: UPDATE foo SET data = data;
+step s2e: SELECT pg_advisory_unlock(0);
+pg_advisory_unlock
+
+t
+step s1a: <... completed>
+error in steps s2e s1a: ERROR: could not serialize access due to concurrent update
+step s1b: COMMIT;
+step s2f: COMMIT;
diff --git a/src/test/isolation/expected/nowait-5.out b/src/test/isolation/expected/nowait-5.out
new file mode 100644
index 00000000000..c88aae5ef60
--- /dev/null
+++ b/src/test/isolation/expected/nowait-5.out
@@ -0,0 +1,37 @@
+Parsed test spec with 3 sessions
+
+starting permutation: sl1_prep upd_getlock sl1_exec upd_doupdate lk1_doforshare upd_releaselock
+step sl1_prep:
+ PREPARE sl1_run AS SELECT id FROM test_nowait WHERE pg_advisory_lock(0) is not null FOR UPDATE NOWAIT;
+
+step upd_getlock:
+ SELECT pg_advisory_lock(0);
+
+pg_advisory_lock
+
+
+step sl1_exec:
+ BEGIN ISOLATION LEVEL READ COMMITTED;
+ EXECUTE sl1_run;
+ SELECT xmin, xmax, ctid, * FROM test_nowait;
+ <waiting ...>
+step upd_doupdate:
+ BEGIN ISOLATION LEVEL READ COMMITTED;
+ UPDATE test_nowait SET value = value WHERE id % 2 = 0;
+ COMMIT;
+
+step lk1_doforshare:
+ BEGIN ISOLATION LEVEL READ COMMITTED;
+ SELECT id FROM test_nowait WHERE id % 2 = 0 FOR SHARE;
+
+id
+
+2
+step upd_releaselock:
+ SELECT pg_advisory_unlock(0);
+
+pg_advisory_unlock
+
+t
+step sl1_exec: <... completed>
+error in steps upd_releaselock sl1_exec: ERROR: could not obtain lock on row in relation "test_nowait"
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 10c89ff1269..3241a91505c 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -22,9 +22,11 @@ test: aborted-keyrevoke
test: multixact-no-deadlock
test: multixact-no-forget
test: propagate-lock-delete
-test: drop-index-concurrently-1
-test: alter-table-1
-test: timeouts
test: nowait
test: nowait-2
test: nowait-3
+test: nowait-4
+test: nowait-5
+test: drop-index-concurrently-1
+test: alter-table-1
+test: timeouts
diff --git a/src/test/isolation/specs/nowait-4.spec b/src/test/isolation/specs/nowait-4.spec
new file mode 100644
index 00000000000..5dbebcabb03
--- /dev/null
+++ b/src/test/isolation/specs/nowait-4.spec
@@ -0,0 +1,30 @@
+# Test NOWAIT with an updated tuple chain.
+
+setup
+{
+ CREATE TABLE foo (
+ id int PRIMARY KEY,
+ data text NOT NULL
+ );
+ INSERT INTO foo VALUES (1, 'x');
+}
+
+teardown
+{
+ DROP TABLE foo;
+}
+
+session "s1"
+setup { BEGIN; }
+step "s1a" { SELECT * FROM foo WHERE pg_advisory_lock(0) IS NOT NULL FOR UPDATE NOWAIT; }
+step "s1b" { COMMIT; }
+
+session "s2"
+step "s2a" { SELECT pg_advisory_lock(0); }
+step "s2b" { UPDATE foo SET data = data; }
+step "s2c" { BEGIN; }
+step "s2d" { UPDATE foo SET data = data; }
+step "s2e" { SELECT pg_advisory_unlock(0); }
+step "s2f" { COMMIT; }
+
+permutation "s2a" "s1a" "s2b" "s2c" "s2d" "s2e" "s1b" "s2f"
diff --git a/src/test/isolation/specs/nowait-5.spec b/src/test/isolation/specs/nowait-5.spec
new file mode 100644
index 00000000000..75e9462fc1f
--- /dev/null
+++ b/src/test/isolation/specs/nowait-5.spec
@@ -0,0 +1,57 @@
+# Test NOWAIT on an updated tuple chain
+
+setup
+{
+
+ DROP TABLE IF EXISTS test_nowait;
+ CREATE TABLE test_nowait (
+ id integer PRIMARY KEY,
+ value integer not null
+ );
+
+ INSERT INTO test_nowait
+ SELECT x,x FROM generate_series(1,2) x;
+}
+
+teardown
+{
+ DROP TABLE test_nowait;
+}
+
+session "sl1"
+step "sl1_prep" {
+ PREPARE sl1_run AS SELECT id FROM test_nowait WHERE pg_advisory_lock(0) is not null FOR UPDATE NOWAIT;
+}
+step "sl1_exec" {
+ BEGIN ISOLATION LEVEL READ COMMITTED;
+ EXECUTE sl1_run;
+ SELECT xmin, xmax, ctid, * FROM test_nowait;
+}
+teardown { COMMIT; }
+
+# A session that's used for an UPDATE of the rows to be locked, for when we're testing ctid
+# chain following.
+session "upd"
+step "upd_getlock" {
+ SELECT pg_advisory_lock(0);
+}
+step "upd_doupdate" {
+ BEGIN ISOLATION LEVEL READ COMMITTED;
+ UPDATE test_nowait SET value = value WHERE id % 2 = 0;
+ COMMIT;
+}
+step "upd_releaselock" {
+ SELECT pg_advisory_unlock(0);
+}
+
+# A session that acquires locks that sl1 is supposed to avoid blocking on
+session "lk1"
+step "lk1_doforshare" {
+ BEGIN ISOLATION LEVEL READ COMMITTED;
+ SELECT id FROM test_nowait WHERE id % 2 = 0 FOR SHARE;
+}
+teardown {
+ COMMIT;
+}
+
+permutation "sl1_prep" "upd_getlock" "sl1_exec" "upd_doupdate" "lk1_doforshare" "upd_releaselock"