diff --git a/src/test/isolation/isolationtester.c b/src/test/isolation/isolationtester.c index 126e185..96d7f17 100644 *** a/src/test/isolation/isolationtester.c --- b/src/test/isolation/isolationtester.c *************** *** 21,26 **** --- 21,27 ---- #endif #include "libpq-fe.h" + #include "pqexpbuffer.h" #include "isolationtester.h" *************** *** 31,37 **** * connections represent spec-defined sessions. */ static PGconn **conns = NULL; ! static const char **backend_ids = NULL; static int nconns = 0; static void run_all_permutations(TestSpec * testspec); --- 32,38 ---- * connections represent spec-defined sessions. */ static PGconn **conns = NULL; ! static const char **backend_pids = NULL; static int nconns = 0; static void run_all_permutations(TestSpec * testspec); *************** *** 67,72 **** main(int argc, char **argv) --- 68,74 ---- TestSpec *testspec; int i; PGresult *res; + PQExpBufferData wait_query; /* * If the user supplies a parameter on the command line, use it as the *************** *** 89,95 **** main(int argc, char **argv) */ nconns = 1 + testspec->nsessions; conns = calloc(nconns, sizeof(PGconn *)); ! backend_ids = calloc(nconns, sizeof(*backend_ids)); for (i = 0; i < nconns; i++) { conns[i] = PQconnectdb(conninfo); --- 91,97 ---- */ nconns = 1 + testspec->nsessions; conns = calloc(nconns, sizeof(PGconn *)); ! backend_pids = calloc(nconns, sizeof(*backend_pids)); for (i = 0; i < nconns; i++) { conns[i] = PQconnectdb(conninfo); *************** *** 112,134 **** main(int argc, char **argv) } PQclear(res); ! /* Get the backend ID for lock wait checking. */ ! res = PQexec(conns[i], "SELECT i FROM pg_stat_get_backend_idset() t(i) " ! "WHERE pg_stat_get_backend_pid(i) = pg_backend_pid()"); if (PQresultStatus(res) == PGRES_TUPLES_OK) { if (PQntuples(res) == 1 && PQnfields(res) == 1) ! backend_ids[i] = strdup(PQgetvalue(res, 0, 0)); else { ! fprintf(stderr, "backend id query returned %d rows and %d columns, expected 1 row and 1 column", PQntuples(res), PQnfields(res)); exit_nicely(); } } else { ! fprintf(stderr, "backend id query failed: %s", PQerrorMessage(conns[i])); exit_nicely(); } --- 114,135 ---- } PQclear(res); ! /* Get the backend pid for lock wait checking. */ ! res = PQexec(conns[i], "SELECT pg_backend_pid()"); if (PQresultStatus(res) == PGRES_TUPLES_OK) { if (PQntuples(res) == 1 && PQnfields(res) == 1) ! backend_pids[i] = strdup(PQgetvalue(res, 0, 0)); else { ! fprintf(stderr, "backend pid query returned %d rows and %d columns, expected 1 row and 1 column", PQntuples(res), PQnfields(res)); exit_nicely(); } } else { ! fprintf(stderr, "backend pid query failed: %s", PQerrorMessage(conns[i])); exit_nicely(); } *************** *** 145,152 **** main(int argc, char **argv) session->steps[stepindex]->session = i; } ! res = PQprepare(conns[0], PREP_WAITING, ! "SELECT 1 WHERE pg_stat_get_backend_waiting($1)", 0, NULL); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "prepare of lock wait query failed: %s", --- 146,232 ---- session->steps[stepindex]->session = i; } ! /* ! * Build the query we'll use to detect lock contention among sessions in ! * the test specification. Most of the time, we could get away with ! * simply checking whether a session is waiting for *any* lock: we don't ! * exactly expect concurrent use of test tables. However, autovacuum will ! * occasionally take AccessExclusiveLock to truncate a table, and we must ! * ignore that transient wait. ! */ ! initPQExpBuffer(&wait_query); ! appendPQExpBufferStr(&wait_query, ! "SELECT 1 FROM pg_locks holder, pg_locks waiter " ! "WHERE NOT waiter.granted AND waiter.pid = $1 " ! "AND holder.granted " ! "AND holder.pid <> $1 AND holder.pid IN ("); ! /* The spec syntax requires at least one session; assume that here. */ ! appendPQExpBuffer(&wait_query, "%s", backend_pids[1]); ! for (i = 2; i < nconns; i++) ! appendPQExpBuffer(&wait_query, ", %s", backend_pids[i]); ! appendPQExpBufferStr(&wait_query, ! ") " ! ! "AND holder.mode = ANY (CASE waiter.mode " ! "WHEN 'AccessShareLock' THEN ARRAY[" ! "'AccessExclusiveLock'] " ! "WHEN 'RowShareLock' THEN ARRAY[" ! "'ExclusiveLock'," ! "'AccessExclusiveLock'] " ! "WHEN 'RowExclusiveLock' THEN ARRAY[" ! "'ShareLock'," ! "'ShareRowExclusiveLock'," ! "'ExclusiveLock'," ! "'AccessExclusiveLock'] " ! "WHEN 'ShareUpdateExclusiveLock' THEN ARRAY[" ! "'ShareUpdateExclusiveLock'," ! "'ShareLock'," ! "'ShareRowExclusiveLock'," ! "'ExclusiveLock'," ! "'AccessExclusiveLock'] " ! "WHEN 'ShareLock' THEN ARRAY[" ! "'RowExclusiveLock'," ! "'ShareUpdateExclusiveLock'," ! "'ShareRowExclusiveLock'," ! "'ExclusiveLock'," ! "'AccessExclusiveLock'] " ! "WHEN 'ShareRowExclusiveLock' THEN ARRAY[" ! "'RowExclusiveLock'," ! "'ShareUpdateExclusiveLock'," ! "'ShareLock'," ! "'ShareRowExclusiveLock'," ! "'ExclusiveLock'," ! "'AccessExclusiveLock'] " ! "WHEN 'ExclusiveLock' THEN ARRAY[" ! "'RowShareLock'," ! "'RowExclusiveLock'," ! "'ShareUpdateExclusiveLock'," ! "'ShareLock'," ! "'ShareRowExclusiveLock'," ! "'ExclusiveLock'," ! "'AccessExclusiveLock'] " ! "WHEN 'AccessExclusiveLock' THEN ARRAY[" ! "'AccessShareLock'," ! "'RowShareLock'," ! "'RowExclusiveLock'," ! "'ShareUpdateExclusiveLock'," ! "'ShareLock'," ! "'ShareRowExclusiveLock'," ! "'ExclusiveLock'," ! "'AccessExclusiveLock'] END) " ! ! "AND holder.locktype IS NOT DISTINCT FROM waiter.locktype " ! "AND holder.database IS NOT DISTINCT FROM waiter.database " ! "AND holder.relation IS NOT DISTINCT FROM waiter.relation " ! "AND holder.page IS NOT DISTINCT FROM waiter.page " ! "AND holder.tuple IS NOT DISTINCT FROM waiter.tuple " ! "AND holder.virtualxid IS NOT DISTINCT FROM waiter.virtualxid " ! "AND holder.transactionid IS NOT DISTINCT FROM waiter.transactionid " ! "AND holder.classid IS NOT DISTINCT FROM waiter.classid " ! "AND holder.objid IS NOT DISTINCT FROM waiter.objid " ! "AND holder.objsubid IS NOT DISTINCT FROM waiter.objsubid "); ! ! res = PQprepare(conns[0], PREP_WAITING, wait_query.data, 0, NULL); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "prepare of lock wait query failed: %s", *************** *** 154,159 **** main(int argc, char **argv) --- 234,240 ---- exit_nicely(); } PQclear(res); + termPQExpBuffer(&wait_query); /* * Run the permutations specified in the spec, or all if none were *************** *** 411,419 **** run_permutation(TestSpec * testspec, int nsteps, Step ** steps) * Our caller already sent the query associated with this step. Wait for it * to either complete or (if given the STEP_NONBLOCK flag) to block while * waiting for a lock. We assume that any lock wait will persist until we ! * have executed additional steps in the permutation. This is not fully ! * robust -- a concurrent autovacuum could briefly take a lock with which we ! * conflict. The risk may be low enough to discount. * * When calling this function on behalf of a given step for a second or later * time, pass the STEP_RETRY flag. This only affects the messages printed. --- 492,498 ---- * Our caller already sent the query associated with this step. Wait for it * to either complete or (if given the STEP_NONBLOCK flag) to block while * waiting for a lock. We assume that any lock wait will persist until we ! * have executed additional steps in the permutation. * * When calling this function on behalf of a given step for a second or later * time, pass the STEP_RETRY flag. This only affects the messages printed. *************** *** 450,456 **** try_complete_step(Step *step, int flags) int ntuples; res = PQexecPrepared(conns[0], PREP_WAITING, 1, ! &backend_ids[step->session + 1], NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK) { --- 529,535 ---- int ntuples; res = PQexecPrepared(conns[0], PREP_WAITING, 1, ! &backend_pids[step->session + 1], NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK) { diff --git a/src/test/isolation/specs/fk-deindex b533d77..9f46c6b 100644 *** a/src/test/isolation/specs/fk-deadlock.spec --- b/src/test/isolation/specs/fk-deadlock.spec *************** *** 19,25 **** teardown } session "s1" ! setup { BEGIN; SET deadlock_timeout = '20ms'; } step "s1i" { INSERT INTO child VALUES (1, 1); } step "s1u" { UPDATE parent SET aux = 'bar'; } step "s1c" { COMMIT; } --- 19,25 ---- } session "s1" ! setup { BEGIN; SET deadlock_timeout = '100ms'; } step "s1i" { INSERT INTO child VALUES (1, 1); } step "s1u" { UPDATE parent SET aux = 'bar'; } step "s1c" { COMMIT; } diff --git a/src/test/isolation/specs/fk-deadlocindex 5653628..a8f1516 100644 *** a/src/test/isolation/specs/fk-deadlock2.spec --- b/src/test/isolation/specs/fk-deadlock2.spec *************** *** 24,30 **** teardown } session "s1" ! setup { BEGIN; SET deadlock_timeout = '20ms'; } step "s1u1" { UPDATE A SET Col1 = 1 WHERE AID = 1; } step "s1u2" { UPDATE B SET Col2 = 1 WHERE BID = 2; } step "s1c" { COMMIT; } --- 24,30 ---- } session "s1" ! setup { BEGIN; SET deadlock_timeout = '100ms'; } step "s1u1" { UPDATE A SET Col1 = 1 WHERE AID = 1; } step "s1u2" { UPDATE B SET Col2 = 1 WHERE BID = 2; } step "s1c" { COMMIT; }