fklocks-tests-harden.patch

text/plain

Filename: fklocks-tests-harden.patch
Type: text/plain
Part: 0
Message: Re: FOR KEY LOCK foreign keys
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; }