2121#endif
2222
2323#include "libpq-fe.h"
24+ #include "pqexpbuffer.h"
2425
2526#include "isolationtester.h"
2627
3132 * connections represent spec-defined sessions.
3233 */
3334static PGconn * * conns = NULL ;
34- static const char * * backend_ids = NULL ;
35+ static const char * * backend_pids = NULL ;
3536static int nconns = 0 ;
3637
3738static void run_all_permutations (TestSpec * testspec );
@@ -67,6 +68,7 @@ main(int argc, char **argv)
6768TestSpec * testspec ;
6869int i ;
6970PGresult * res ;
71+ PQExpBufferData wait_query ;
7072
7173/*
7274 * If the user supplies a parameter on the command line, use it as the
@@ -89,7 +91,7 @@ main(int argc, char **argv)
8991 */
9092nconns = 1 + testspec -> nsessions ;
9193conns = calloc (nconns ,sizeof (PGconn * ));
92- backend_ids = calloc (nconns ,sizeof (* backend_ids ));
94+ backend_pids = calloc (nconns ,sizeof (* backend_pids ));
9395for (i = 0 ;i < nconns ;i ++ )
9496{
9597conns [i ]= PQconnectdb (conninfo );
@@ -112,23 +114,22 @@ main(int argc, char **argv)
112114}
113115PQclear (res );
114116
115- /* Get the backend ID for lock wait checking. */
116- res = PQexec (conns [i ],"SELECT i FROM pg_stat_get_backend_idset() t(i) "
117- "WHERE pg_stat_get_backend_pid(i) = pg_backend_pid()" );
117+ /* Get the backend pid for lock wait checking. */
118+ res = PQexec (conns [i ],"SELECT pg_backend_pid()" );
118119if (PQresultStatus (res )== PGRES_TUPLES_OK )
119120{
120121if (PQntuples (res )== 1 && PQnfields (res )== 1 )
121- backend_ids [i ]= strdup (PQgetvalue (res ,0 ,0 ));
122+ backend_pids [i ]= strdup (PQgetvalue (res ,0 ,0 ));
122123else
123124{
124- fprintf (stderr ,"backendid query returned %d rows and %d columns, expected 1 row and 1 column" ,
125+ fprintf (stderr ,"backendpid query returned %d rows and %d columns, expected 1 row and 1 column" ,
125126PQntuples (res ),PQnfields (res ));
126127exit_nicely ();
127128}
128129}
129130else
130131{
131- fprintf (stderr ,"backendid query failed: %s" ,
132+ fprintf (stderr ,"backendpid query failed: %s" ,
132133PQerrorMessage (conns [i ]));
133134exit_nicely ();
134135}
@@ -145,15 +146,95 @@ main(int argc, char **argv)
145146session -> steps [stepindex ]-> session = i ;
146147}
147148
148- res = PQprepare (conns [0 ],PREP_WAITING ,
149- "SELECT 1 WHERE pg_stat_get_backend_waiting($1)" ,0 ,NULL );
149+ /*
150+ * Build the query we'll use to detect lock contention among sessions in
151+ * the test specification. Most of the time, we could get away with
152+ * simply checking whether a session is waiting for *any* lock: we don't
153+ * exactly expect concurrent use of test tables. However, autovacuum will
154+ * occasionally take AccessExclusiveLock to truncate a table, and we must
155+ * ignore that transient wait.
156+ */
157+ initPQExpBuffer (& wait_query );
158+ appendPQExpBufferStr (& wait_query ,
159+ "SELECT 1 FROM pg_locks holder, pg_locks waiter "
160+ "WHERE NOT waiter.granted AND waiter.pid = $1 "
161+ "AND holder.granted "
162+ "AND holder.pid <> $1 AND holder.pid IN (" );
163+ /* The spec syntax requires at least one session; assume that here. */
164+ appendPQExpBuffer (& wait_query ,"%s" ,backend_pids [1 ]);
165+ for (i = 2 ;i < nconns ;i ++ )
166+ appendPQExpBuffer (& wait_query ,", %s" ,backend_pids [i ]);
167+ appendPQExpBufferStr (& wait_query ,
168+ ") "
169+
170+ "AND holder.mode = ANY (CASE waiter.mode "
171+ "WHEN 'AccessShareLock' THEN ARRAY["
172+ "'AccessExclusiveLock'] "
173+ "WHEN 'RowShareLock' THEN ARRAY["
174+ "'ExclusiveLock',"
175+ "'AccessExclusiveLock'] "
176+ "WHEN 'RowExclusiveLock' THEN ARRAY["
177+ "'ShareLock',"
178+ "'ShareRowExclusiveLock',"
179+ "'ExclusiveLock',"
180+ "'AccessExclusiveLock'] "
181+ "WHEN 'ShareUpdateExclusiveLock' THEN ARRAY["
182+ "'ShareUpdateExclusiveLock',"
183+ "'ShareLock',"
184+ "'ShareRowExclusiveLock',"
185+ "'ExclusiveLock',"
186+ "'AccessExclusiveLock'] "
187+ "WHEN 'ShareLock' THEN ARRAY["
188+ "'RowExclusiveLock',"
189+ "'ShareUpdateExclusiveLock',"
190+ "'ShareRowExclusiveLock',"
191+ "'ExclusiveLock',"
192+ "'AccessExclusiveLock'] "
193+ "WHEN 'ShareRowExclusiveLock' THEN ARRAY["
194+ "'RowExclusiveLock',"
195+ "'ShareUpdateExclusiveLock',"
196+ "'ShareLock',"
197+ "'ShareRowExclusiveLock',"
198+ "'ExclusiveLock',"
199+ "'AccessExclusiveLock'] "
200+ "WHEN 'ExclusiveLock' THEN ARRAY["
201+ "'RowShareLock',"
202+ "'RowExclusiveLock',"
203+ "'ShareUpdateExclusiveLock',"
204+ "'ShareLock',"
205+ "'ShareRowExclusiveLock',"
206+ "'ExclusiveLock',"
207+ "'AccessExclusiveLock'] "
208+ "WHEN 'AccessExclusiveLock' THEN ARRAY["
209+ "'AccessShareLock',"
210+ "'RowShareLock',"
211+ "'RowExclusiveLock',"
212+ "'ShareUpdateExclusiveLock',"
213+ "'ShareLock',"
214+ "'ShareRowExclusiveLock',"
215+ "'ExclusiveLock',"
216+ "'AccessExclusiveLock'] END) "
217+
218+ "AND holder.locktype IS NOT DISTINCT FROM waiter.locktype "
219+ "AND holder.database IS NOT DISTINCT FROM waiter.database "
220+ "AND holder.relation IS NOT DISTINCT FROM waiter.relation "
221+ "AND holder.page IS NOT DISTINCT FROM waiter.page "
222+ "AND holder.tuple IS NOT DISTINCT FROM waiter.tuple "
223+ "AND holder.virtualxid IS NOT DISTINCT FROM waiter.virtualxid "
224+ "AND holder.transactionid IS NOT DISTINCT FROM waiter.transactionid "
225+ "AND holder.classid IS NOT DISTINCT FROM waiter.classid "
226+ "AND holder.objid IS NOT DISTINCT FROM waiter.objid "
227+ "AND holder.objsubid IS NOT DISTINCT FROM waiter.objsubid " );
228+
229+ res = PQprepare (conns [0 ],PREP_WAITING ,wait_query .data ,0 ,NULL );
150230if (PQresultStatus (res )!= PGRES_COMMAND_OK )
151231{
152232fprintf (stderr ,"prepare of lock wait query failed: %s" ,
153233PQerrorMessage (conns [0 ]));
154234exit_nicely ();
155235}
156236PQclear (res );
237+ termPQExpBuffer (& wait_query );
157238
158239/*
159240 * Run the permutations specified in the spec, or all if none were
@@ -411,9 +492,7 @@ run_permutation(TestSpec * testspec, int nsteps, Step ** steps)
411492 * Our caller already sent the query associated with this step. Wait for it
412493 * to either complete or (if given the STEP_NONBLOCK flag) to block while
413494 * waiting for a lock. We assume that any lock wait will persist until we
414- * have executed additional steps in the permutation. This is not fully
415- * robust -- a concurrent autovacuum could briefly take a lock with which we
416- * conflict. The risk may be low enough to discount.
495+ * have executed additional steps in the permutation.
417496 *
418497 * When calling this function on behalf of a given step for a second or later
419498 * time, pass the STEP_RETRY flag. This only affects the messages printed.
@@ -450,7 +529,7 @@ try_complete_step(Step *step, int flags)
450529int ntuples ;
451530
452531res = PQexecPrepared (conns [0 ],PREP_WAITING ,1 ,
453- & backend_ids [step -> session + 1 ],
532+ & backend_pids [step -> session + 1 ],
454533NULL ,NULL ,0 );
455534if (PQresultStatus (res )!= PGRES_TUPLES_OK )
456535{