PostgreSQL Source Code  git master
isolationtester.c File Reference
#include "postgres_fe.h"
#include <sys/time.h>
#include "datatype/timestamp.h"
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include "pg_getopt.h"
#include "isolationtester.h"
Include dependency graph for isolationtester.c:

Go to the source code of this file.

Macros

#define PREP_WAITING   "isolationtester_waiting"
 
#define STEP_NONBLOCK   0x1 /* return 0 as soon as cmd waits for a lock */
 
#define STEP_RETRY   0x2 /* this is a retry of a previously-waiting cmd */
 

Functions

static void run_testspec (TestSpec *testspec)
 
static void run_all_permutations (TestSpec *testspec)
 
static void run_all_permutations_recurse (TestSpec *testspec, int nsteps, Step **steps)
 
static void run_named_permutations (TestSpec *testspec)
 
static void run_permutation (TestSpec *testspec, int nsteps, Step **steps)
 
static bool try_complete_step (TestSpec *testspec, Step *step, int flags)
 
static int step_qsort_cmp (const void *a, const void *b)
 
static int step_bsearch_cmp (const void *a, const void *b)
 
static void printResultSet (PGresult *res)
 
static void isotesterNoticeProcessor (void *arg, const char *message)
 
static void blackholeNoticeProcessor (void *arg, const char *message)
 
static void disconnect_atexit (void)
 
int main (int argc, char **argv)
 
static void report_error_message (Step *step)
 
static void report_multiple_error_messages (Step *step, int nextra, Step **extrastep)
 

Variables

static PGconn ** conns = NULL
 
static int * backend_pids = NULL
 
static const char ** backend_pid_strs = NULL
 
static int nconns = 0
 
static int * piles
 

Macro Definition Documentation

◆ PREP_WAITING

#define PREP_WAITING   "isolationtester_waiting"

Definition at line 22 of file isolationtester.c.

Referenced by main(), and try_complete_step().

◆ STEP_NONBLOCK

#define STEP_NONBLOCK   0x1 /* return 0 as soon as cmd waits for a lock */

Definition at line 41 of file isolationtester.c.

Referenced by run_permutation(), and try_complete_step().

◆ STEP_RETRY

#define STEP_RETRY   0x2 /* this is a retry of a previously-waiting cmd */

Definition at line 42 of file isolationtester.c.

Referenced by run_permutation(), and try_complete_step().

Function Documentation

◆ blackholeNoticeProcessor()

static void blackholeNoticeProcessor ( void *  arg,
const char *  message 
)
static

Definition at line 924 of file isolationtester.c.

Referenced by main().

925 {
926  /* do nothing */
927 }

◆ disconnect_atexit()

static void disconnect_atexit ( void  )
static

Definition at line 53 of file isolationtester.c.

References i, nconns, and PQfinish().

Referenced by main().

54 {
55  int i;
56 
57  for (i = 0; i < nconns; i++)
58  if (conns[i])
59  PQfinish(conns[i]);
60 }
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4098
static PGconn ** conns
int i
static int nconns

◆ isotesterNoticeProcessor()

static void isotesterNoticeProcessor ( void *  arg,
const char *  message 
)
static

Definition at line 917 of file isolationtester.c.

References printf.

Referenced by main().

918 {
919  printf("%s: %s", (char *) arg, message);
920 }
#define printf(...)
Definition: port.h:198
void * arg

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 63 of file isolationtester.c.

References TestSpec::allsteps, appendPQExpBuffer(), appendPQExpBufferStr(), backend_pid_strs, backend_pids, blackholeNoticeProcessor(), CONNECTION_OK, PQExpBufferData::data, disconnect_atexit(), EXIT_FAILURE, fprintf, getopt(), i, initPQExpBuffer(), isotesterNoticeProcessor(), TestSpec::nallsteps, Session::name, Step::name, nconns, TestSpec::nsessions, Session::nsteps, optind, parseresult, pg_malloc(), pg_malloc0(), PGRES_COMMAND_OK, PQbackendPID(), PQclear(), PQconnectdb(), PQerrorMessage(), PQprepare(), PQresultStatus(), PQsetNoticeProcessor(), PQstatus(), PREP_WAITING, printf, psprintf(), qsort, run_testspec(), Step::session, TestSpec::sessions, spec_yyparse(), generate_unaccent_rules::stdout, step_qsort_cmp(), Session::steps, and termPQExpBuffer().

64 {
65  const char *conninfo;
66  TestSpec *testspec;
67  int i,
68  j;
69  int n;
70  PGresult *res;
71  PQExpBufferData wait_query;
72  int opt;
73  int nallsteps;
74  Step **allsteps;
75 
76  while ((opt = getopt(argc, argv, "V")) != -1)
77  {
78  switch (opt)
79  {
80  case 'V':
81  puts("isolationtester (PostgreSQL) " PG_VERSION);
82  exit(0);
83  default:
84  fprintf(stderr, "Usage: isolationtester [CONNINFO]\n");
85  return EXIT_FAILURE;
86  }
87  }
88 
89  /*
90  * Make stdout unbuffered to match stderr; and ensure stderr is unbuffered
91  * too, which it should already be everywhere except sometimes in Windows.
92  */
93  setbuf(stdout, NULL);
94  setbuf(stderr, NULL);
95 
96  /*
97  * If the user supplies a non-option parameter on the command line, use it
98  * as the conninfo string; otherwise default to setting dbname=postgres
99  * and using environment variables or defaults for all other connection
100  * parameters.
101  */
102  if (argc > optind)
103  conninfo = argv[optind];
104  else
105  conninfo = "dbname = postgres";
106 
107  /* Read the test spec from stdin */
108  spec_yyparse();
109  testspec = &parseresult;
110 
111  /* Create a lookup table of all steps. */
112  nallsteps = 0;
113  for (i = 0; i < testspec->nsessions; i++)
114  nallsteps += testspec->sessions[i]->nsteps;
115 
116  allsteps = pg_malloc(nallsteps * sizeof(Step *));
117 
118  n = 0;
119  for (i = 0; i < testspec->nsessions; i++)
120  {
121  for (j = 0; j < testspec->sessions[i]->nsteps; j++)
122  allsteps[n++] = testspec->sessions[i]->steps[j];
123  }
124 
125  qsort(allsteps, nallsteps, sizeof(Step *), &step_qsort_cmp);
126  testspec->nallsteps = nallsteps;
127  testspec->allsteps = allsteps;
128 
129  /* Verify that all step names are unique */
130  for (i = 1; i < testspec->nallsteps; i++)
131  {
132  if (strcmp(testspec->allsteps[i - 1]->name,
133  testspec->allsteps[i]->name) == 0)
134  {
135  fprintf(stderr, "duplicate step name: %s\n",
136  testspec->allsteps[i]->name);
137  exit(1);
138  }
139  }
140 
141  printf("Parsed test spec with %d sessions\n", testspec->nsessions);
142 
143  /*
144  * Establish connections to the database, one for each session and an
145  * extra for lock wait detection and global work.
146  */
147  nconns = 1 + testspec->nsessions;
148  conns = (PGconn **) pg_malloc0(nconns * sizeof(PGconn *));
151  atexit(disconnect_atexit);
152 
153  for (i = 0; i < nconns; i++)
154  {
155  conns[i] = PQconnectdb(conninfo);
156  if (PQstatus(conns[i]) != CONNECTION_OK)
157  {
158  fprintf(stderr, "Connection %d to database failed: %s",
159  i, PQerrorMessage(conns[i]));
160  exit(1);
161  }
162 
163  /*
164  * Set up notice processors for the user-defined connections, so that
165  * messages can get printed prefixed with the session names. The
166  * control connection gets a "blackhole" processor instead (hides all
167  * messages).
168  */
169  if (i != 0)
172  (void *) (testspec->sessions[i - 1]->name));
173  else
176  NULL);
177 
178  /* Save each connection's backend PID for subsequent use. */
181  }
182 
183  /* Set the session index fields in steps. */
184  for (i = 0; i < testspec->nsessions; i++)
185  {
186  Session *session = testspec->sessions[i];
187  int stepindex;
188 
189  for (stepindex = 0; stepindex < session->nsteps; stepindex++)
190  session->steps[stepindex]->session = i;
191  }
192 
193  /*
194  * Build the query we'll use to detect lock contention among sessions in
195  * the test specification. Most of the time, we could get away with
196  * simply checking whether a session is waiting for *any* lock: we don't
197  * exactly expect concurrent use of test tables. However, autovacuum will
198  * occasionally take AccessExclusiveLock to truncate a table, and we must
199  * ignore that transient wait.
200  */
201  initPQExpBuffer(&wait_query);
202  appendPQExpBufferStr(&wait_query,
203  "SELECT pg_catalog.pg_isolation_test_session_is_blocked($1, '{");
204  /* The spec syntax requires at least one session; assume that here. */
205  appendPQExpBufferStr(&wait_query, backend_pid_strs[1]);
206  for (i = 2; i < nconns; i++)
207  appendPQExpBuffer(&wait_query, ",%s", backend_pid_strs[i]);
208  appendPQExpBufferStr(&wait_query, "}')");
209 
210  res = PQprepare(conns[0], PREP_WAITING, wait_query.data, 0, NULL);
211  if (PQresultStatus(res) != PGRES_COMMAND_OK)
212  {
213  fprintf(stderr, "prepare of lock wait query failed: %s",
214  PQerrorMessage(conns[0]));
215  exit(1);
216  }
217  PQclear(res);
218  termPQExpBuffer(&wait_query);
219 
220  /*
221  * Run the permutations specified in the spec, or all if none were
222  * explicitly specified.
223  */
224  run_testspec(testspec);
225 
226  return 0;
227 }
PGresult * PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
Definition: fe-exec.c:1984
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6596
PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
Definition: fe-connect.c:6772
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
Session ** sessions
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define printf(...)
Definition: port.h:198
#define fprintf
Definition: port.h:196
static int step_qsort_cmp(const void *a, const void *b)
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition: getopt.c:71
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2693
char * name
static void blackholeNoticeProcessor(void *arg, const char *message)
int optind
Definition: getopt.c:50
static PGconn ** conns
char * name
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
static int * backend_pids
static void disconnect_atexit(void)
int PQbackendPID(const PGconn *conn)
Definition: fe-connect.c:6622
void PQclear(PGresult *res)
Definition: fe-exec.c:695
static const char ** backend_pid_strs
static void isotesterNoticeProcessor(void *arg, const char *message)
#define PREP_WAITING
Step ** steps
static void run_testspec(TestSpec *testspec)
int i
Step ** allsteps
#define EXIT_FAILURE
Definition: settings.h:154
int session
TestSpec parseresult
#define qsort(a, b, c, d)
Definition: port.h:492
ConnStatusType PQstatus(const PGconn *conn)
Definition: fe-connect.c:6543
static int nconns
int spec_yyparse(void)
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92
PGconn * PQconnectdb(const char *conninfo)
Definition: fe-connect.c:678

◆ printResultSet()

static void printResultSet ( PGresult res)
static

Definition at line 894 of file isolationtester.c.

References i, PQfname(), PQgetvalue(), PQnfields(), PQntuples(), and printf.

Referenced by run_permutation(), and try_complete_step().

895 {
896  int nFields;
897  int i,
898  j;
899 
900  /* first, print out the attribute names */
901  nFields = PQnfields(res);
902  for (i = 0; i < nFields; i++)
903  printf("%-15s", PQfname(res, i));
904  printf("\n\n");
905 
906  /* next, print out the rows */
907  for (i = 0; i < PQntuples(res); i++)
908  {
909  for (j = 0; j < nFields; j++)
910  printf("%-15s", PQgetvalue(res, i, j));
911  printf("\n");
912  }
913 }
int PQnfields(const PGresult *res)
Definition: fe-exec.c:2778
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3164
char * PQfname(const PGresult *res, int field_num)
Definition: fe-exec.c:2856
#define printf(...)
Definition: port.h:198
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2770
int i

◆ report_error_message()

static void report_error_message ( Step step)
static

Definition at line 380 of file isolationtester.c.

References Step::errormsg, fprintf, free, and generate_unaccent_rules::stdout.

Referenced by report_multiple_error_messages(), and run_permutation().

381 {
382  if (step->errormsg)
383  {
384  fprintf(stdout, "%s\n", step->errormsg);
385  free(step->errormsg);
386  step->errormsg = NULL;
387  }
388 }
#define fprintf
Definition: port.h:196
char * errormsg
#define free(a)
Definition: header.h:65

◆ report_multiple_error_messages()

static void report_multiple_error_messages ( Step step,
int  nextra,
Step **  extrastep 
)
static

Definition at line 397 of file isolationtester.c.

References appendPQExpBuffer(), appendPQExpBufferStr(), PQExpBufferData::data, Step::errormsg, fprintf, free, initPQExpBuffer(), Step::name, name, report_error_message(), generate_unaccent_rules::stdout, and termPQExpBuffer().

Referenced by run_permutation().

398 {
399  PQExpBufferData buffer;
400  int n;
401 
402  if (nextra == 0)
403  {
404  report_error_message(step);
405  return;
406  }
407 
408  initPQExpBuffer(&buffer);
409  appendPQExpBufferStr(&buffer, step->name);
410 
411  for (n = 0; n < nextra; ++n)
412  appendPQExpBuffer(&buffer, " %s", extrastep[n]->name);
413 
414  if (step->errormsg)
415  {
416  fprintf(stdout, "error in steps %s: %s\n", buffer.data,
417  step->errormsg);
418  free(step->errormsg);
419  step->errormsg = NULL;
420  }
421 
422  for (n = 0; n < nextra; ++n)
423  {
424  if (extrastep[n]->errormsg == NULL)
425  continue;
426  fprintf(stdout, "error in steps %s: %s\n",
427  buffer.data, extrastep[n]->errormsg);
428  free(extrastep[n]->errormsg);
429  extrastep[n]->errormsg = NULL;
430  }
431 
432  termPQExpBuffer(&buffer);
433 }
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
#define fprintf
Definition: port.h:196
char * name
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
char * errormsg
#define free(a)
Definition: header.h:65
const char * name
Definition: encode.c:521
static void report_error_message(Step *step)
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92

◆ run_all_permutations()

static void run_all_permutations ( TestSpec testspec)
static

Definition at line 261 of file isolationtester.c.

References i, TestSpec::nsessions, Session::nsteps, pg_malloc(), piles, run_all_permutations_recurse(), and TestSpec::sessions.

Referenced by run_testspec().

262 {
263  int nsteps;
264  int i;
265  Step **steps;
266 
267  /* Count the total number of steps in all sessions */
268  nsteps = 0;
269  for (i = 0; i < testspec->nsessions; i++)
270  nsteps += testspec->sessions[i]->nsteps;
271 
272  steps = pg_malloc(sizeof(Step *) * nsteps);
273 
274  /*
275  * To generate the permutations, we conceptually put the steps of each
276  * session on a pile. To generate a permutation, we pick steps from the
277  * piles until all piles are empty. By picking steps from piles in
278  * different order, we get different permutations.
279  *
280  * A pile is actually just an integer which tells how many steps we've
281  * already picked from this pile.
282  */
283  piles = pg_malloc(sizeof(int) * testspec->nsessions);
284  for (i = 0; i < testspec->nsessions; i++)
285  piles[i] = 0;
286 
287  run_all_permutations_recurse(testspec, 0, steps);
288 }
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
Session ** sessions
static int * piles
static void run_all_permutations_recurse(TestSpec *testspec, int nsteps, Step **steps)
int i

◆ run_all_permutations_recurse()

static void run_all_permutations_recurse ( TestSpec testspec,
int  nsteps,
Step **  steps 
)
static

Definition at line 291 of file isolationtester.c.

References i, TestSpec::nsessions, Session::nsteps, piles, run_permutation(), TestSpec::sessions, and Session::steps.

Referenced by run_all_permutations().

292 {
293  int i;
294  int found = 0;
295 
296  for (i = 0; i < testspec->nsessions; i++)
297  {
298  /* If there's any more steps in this pile, pick it and recurse */
299  if (piles[i] < testspec->sessions[i]->nsteps)
300  {
301  steps[nsteps] = testspec->sessions[i]->steps[piles[i]];
302  piles[i]++;
303 
304  run_all_permutations_recurse(testspec, nsteps + 1, steps);
305 
306  piles[i]--;
307 
308  found = 1;
309  }
310  }
311 
312  /* If all the piles were empty, this permutation is completed. Run it */
313  if (!found)
314  run_permutation(testspec, nsteps, steps);
315 }
Session ** sessions
static int * piles
static void run_permutation(TestSpec *testspec, int nsteps, Step **steps)
static void run_all_permutations_recurse(TestSpec *testspec, int nsteps, Step **steps)
Step ** steps
int i

◆ run_named_permutations()

static void run_named_permutations ( TestSpec testspec)
static

Definition at line 321 of file isolationtester.c.

References TestSpec::allsteps, fprintf, free, i, TestSpec::nallsteps, TestSpec::npermutations, Permutation::nsteps, TestSpec::permutations, pg_malloc(), run_permutation(), step_bsearch_cmp(), and Permutation::stepnames.

Referenced by run_testspec().

322 {
323  int i,
324  j;
325 
326  for (i = 0; i < testspec->npermutations; i++)
327  {
328  Permutation *p = testspec->permutations[i];
329  Step **steps;
330 
331  steps = pg_malloc(p->nsteps * sizeof(Step *));
332 
333  /* Find all the named steps using the lookup table */
334  for (j = 0; j < p->nsteps; j++)
335  {
336  Step **this = (Step **) bsearch(p->stepnames[j],
337  testspec->allsteps,
338  testspec->nallsteps,
339  sizeof(Step *),
341 
342  if (this == NULL)
343  {
344  fprintf(stderr, "undefined step \"%s\" specified in permutation\n",
345  p->stepnames[j]);
346  exit(1);
347  }
348  steps[j] = *this;
349  }
350 
351  /* And run them */
352  run_permutation(testspec, p->nsteps, steps);
353 
354  free(steps);
355  }
356 }
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
Permutation ** permutations
#define fprintf
Definition: port.h:196
int npermutations
static int step_bsearch_cmp(const void *a, const void *b)
char ** stepnames
#define free(a)
Definition: header.h:65
static void run_permutation(TestSpec *testspec, int nsteps, Step **steps)
int i
Step ** allsteps

◆ run_permutation()

static void run_permutation ( TestSpec testspec,
int  nsteps,
Step **  steps 
)
static

Definition at line 439 of file isolationtester.c.

References conn, fprintf, free, i, memmove, Session::name, Step::name, name, TestSpec::nsessions, TestSpec::nsetupsqls, pg_malloc(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQerrorMessage(), PQexec(), PQresultStatus(), PQsendQuery(), printf, printResultSet(), report_error_message(), report_multiple_error_messages(), Step::session, TestSpec::sessions, Session::setupsql, TestSpec::setupsqls, Step::sql, generate_unaccent_rules::stdout, STEP_NONBLOCK, STEP_RETRY, Session::teardownsql, TestSpec::teardownsql, try_complete_step(), Step::used, and waiting.

Referenced by run_all_permutations_recurse(), and run_named_permutations().

440 {
441  PGresult *res;
442  int i;
443  int w;
444  int nwaiting = 0;
445  int nerrorstep = 0;
446  Step **waiting;
447  Step **errorstep;
448 
449  waiting = pg_malloc(sizeof(Step *) * testspec->nsessions);
450  errorstep = pg_malloc(sizeof(Step *) * testspec->nsessions);
451 
452  printf("\nstarting permutation:");
453  for (i = 0; i < nsteps; i++)
454  {
455  /* Track the permutation as in-use */
456  steps[i]->used = true;
457  printf(" %s", steps[i]->name);
458  }
459  printf("\n");
460 
461  /* Perform setup */
462  for (i = 0; i < testspec->nsetupsqls; i++)
463  {
464  res = PQexec(conns[0], testspec->setupsqls[i]);
465  if (PQresultStatus(res) == PGRES_TUPLES_OK)
466  {
467  printResultSet(res);
468  }
469  else if (PQresultStatus(res) != PGRES_COMMAND_OK)
470  {
471  fprintf(stderr, "setup failed: %s", PQerrorMessage(conns[0]));
472  exit(1);
473  }
474  PQclear(res);
475  }
476 
477  /* Perform per-session setup */
478  for (i = 0; i < testspec->nsessions; i++)
479  {
480  if (testspec->sessions[i]->setupsql)
481  {
482  res = PQexec(conns[i + 1], testspec->sessions[i]->setupsql);
483  if (PQresultStatus(res) == PGRES_TUPLES_OK)
484  {
485  printResultSet(res);
486  }
487  else if (PQresultStatus(res) != PGRES_COMMAND_OK)
488  {
489  fprintf(stderr, "setup of session %s failed: %s",
490  testspec->sessions[i]->name,
491  PQerrorMessage(conns[i + 1]));
492  exit(1);
493  }
494  PQclear(res);
495  }
496  }
497 
498  /* Perform steps */
499  for (i = 0; i < nsteps; i++)
500  {
501  Step *step = steps[i];
502  PGconn *conn = conns[1 + step->session];
503  Step *oldstep = NULL;
504  bool mustwait;
505 
506  /*
507  * Check whether the session that needs to perform the next step is
508  * still blocked on an earlier step. If so, wait for it to finish.
509  *
510  * (In older versions of this tool, we allowed precisely one session
511  * to be waiting at a time. If we reached a step that required that
512  * session to execute the next command, we would declare the whole
513  * permutation invalid, cancel everything, and move on to the next
514  * one. Unfortunately, that made it impossible to test the deadlock
515  * detector using this framework, unless the number of processes
516  * involved in the deadlock was precisely two. We now assume that if
517  * we reach a step that is still blocked, we need to wait for it to
518  * unblock itself.)
519  */
520  for (w = 0; w < nwaiting; ++w)
521  {
522  if (step->session == waiting[w]->session)
523  {
524  oldstep = waiting[w];
525 
526  /* Wait for previous step on this connection. */
527  try_complete_step(testspec, oldstep, STEP_RETRY);
528 
529  /* Remove that step from the waiting[] array. */
530  if (w + 1 < nwaiting)
531  memmove(&waiting[w], &waiting[w + 1],
532  (nwaiting - (w + 1)) * sizeof(Step *));
533  nwaiting--;
534 
535  break;
536  }
537  }
538  if (oldstep != NULL)
539  {
540  /*
541  * Check for completion of any steps that were previously waiting.
542  * Remove any that have completed from waiting[], and include them
543  * in the list for report_multiple_error_messages().
544  */
545  w = 0;
546  nerrorstep = 0;
547  while (w < nwaiting)
548  {
549  if (try_complete_step(testspec, waiting[w],
551  {
552  /* Still blocked on a lock, leave it alone. */
553  w++;
554  }
555  else
556  {
557  /* This one finished, too! */
558  errorstep[nerrorstep++] = waiting[w];
559  if (w + 1 < nwaiting)
560  memmove(&waiting[w], &waiting[w + 1],
561  (nwaiting - (w + 1)) * sizeof(Step *));
562  nwaiting--;
563  }
564  }
565 
566  /* Report all errors together. */
567  report_multiple_error_messages(oldstep, nerrorstep, errorstep);
568  }
569 
570  /* Send the query for this step. */
571  if (!PQsendQuery(conn, step->sql))
572  {
573  fprintf(stdout, "failed to send query for step %s: %s\n",
574  step->name, PQerrorMessage(conn));
575  exit(1);
576  }
577 
578  /* Try to complete this step without blocking. */
579  mustwait = try_complete_step(testspec, step, STEP_NONBLOCK);
580 
581  /* Check for completion of any steps that were previously waiting. */
582  w = 0;
583  nerrorstep = 0;
584  while (w < nwaiting)
585  {
586  if (try_complete_step(testspec, waiting[w],
588  w++;
589  else
590  {
591  errorstep[nerrorstep++] = waiting[w];
592  if (w + 1 < nwaiting)
593  memmove(&waiting[w], &waiting[w + 1],
594  (nwaiting - (w + 1)) * sizeof(Step *));
595  nwaiting--;
596  }
597  }
598 
599  /* Report any error from this step, and any steps that it unblocked. */
600  report_multiple_error_messages(step, nerrorstep, errorstep);
601 
602  /* If this step is waiting, add it to the array of waiters. */
603  if (mustwait)
604  waiting[nwaiting++] = step;
605  }
606 
607  /* Wait for any remaining queries. */
608  for (w = 0; w < nwaiting; ++w)
609  {
610  try_complete_step(testspec, waiting[w], STEP_RETRY);
611  report_error_message(waiting[w]);
612  }
613 
614  /* Perform per-session teardown */
615  for (i = 0; i < testspec->nsessions; i++)
616  {
617  if (testspec->sessions[i]->teardownsql)
618  {
619  res = PQexec(conns[i + 1], testspec->sessions[i]->teardownsql);
620  if (PQresultStatus(res) == PGRES_TUPLES_OK)
621  {
622  printResultSet(res);
623  }
624  else if (PQresultStatus(res) != PGRES_COMMAND_OK)
625  {
626  fprintf(stderr, "teardown of session %s failed: %s",
627  testspec->sessions[i]->name,
628  PQerrorMessage(conns[i + 1]));
629  /* don't exit on teardown failure */
630  }
631  PQclear(res);
632  }
633  }
634 
635  /* Perform teardown */
636  if (testspec->teardownsql)
637  {
638  res = PQexec(conns[0], testspec->teardownsql);
639  if (PQresultStatus(res) == PGRES_TUPLES_OK)
640  {
641  printResultSet(res);
642  }
643  else if (PQresultStatus(res) != PGRES_COMMAND_OK)
644  {
645  fprintf(stderr, "teardown failed: %s",
646  PQerrorMessage(conns[0]));
647  /* don't exit on teardown failure */
648  }
649  PQclear(res);
650  }
651 
652  free(waiting);
653  free(errorstep);
654 }
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6596
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
Session ** sessions
char ** setupsqls
bool used
char * teardownsql
static void report_multiple_error_messages(Step *step, int nextra, Step **extrastep)
#define printf(...)
Definition: port.h:198
char * setupsql
#define fprintf
Definition: port.h:196
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2693
char * name
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1235
static PGconn ** conns
static bool try_complete_step(TestSpec *testspec, Step *step, int flags)
char * name
char * teardownsql
PGconn * conn
Definition: streamutil.c:56
#define memmove(d, s, c)
Definition: c.h:1238
#define STEP_RETRY
void PQclear(PGresult *res)
Definition: fe-exec.c:695
#define STEP_NONBLOCK
#define free(a)
Definition: header.h:65
char * sql
const char * name
Definition: encode.c:521
static void report_error_message(Step *step)
int i
PGresult * PQexec(PGconn *conn, const char *query)
Definition: fe-exec.c:1940
int session
static volatile sig_atomic_t waiting
Definition: latch.c:123
static void printResultSet(PGresult *res)

◆ run_testspec()

static void run_testspec ( TestSpec testspec)
static

Definition at line 236 of file isolationtester.c.

References TestSpec::allsteps, fprintf, i, TestSpec::nallsteps, Step::name, TestSpec::permutations, run_all_permutations(), run_named_permutations(), and Step::used.

Referenced by main().

237 {
238  int i;
239 
240  if (testspec->permutations)
241  run_named_permutations(testspec);
242  else
243  run_all_permutations(testspec);
244 
245  /*
246  * Verify that all steps have been used, complaining about anything
247  * defined but not used.
248  */
249  for (i = 0; i < testspec->nallsteps; i++)
250  {
251  if (!testspec->allsteps[i]->used)
252  fprintf(stderr, "unused step name: %s\n",
253  testspec->allsteps[i]->name);
254  }
255 }
static void run_named_permutations(TestSpec *testspec)
bool used
static void run_all_permutations(TestSpec *testspec)
Permutation ** permutations
#define fprintf
Definition: port.h:196
char * name
int i
Step ** allsteps

◆ step_bsearch_cmp()

static int step_bsearch_cmp ( const void *  a,
const void *  b 
)
static

Definition at line 368 of file isolationtester.c.

References Step::name.

Referenced by run_named_permutations().

369 {
370  char *stepname = (char *) a;
371  Step *step = *((Step **) b);
372 
373  return strcmp(stepname, step->name);
374 }
char * name

◆ step_qsort_cmp()

static int step_qsort_cmp ( const void *  a,
const void *  b 
)
static

Definition at line 359 of file isolationtester.c.

References Step::name.

Referenced by main().

360 {
361  Step *stepa = *((Step **) a);
362  Step *stepb = *((Step **) b);
363 
364  return strcmp(stepa->name, stepb->name);
365 }
char * name

◆ try_complete_step()

static bool try_complete_step ( TestSpec testspec,
Step step,
int  flags 
)
static

Definition at line 673 of file isolationtester.c.

References backend_pid_strs, backend_pids, pgNotify::be_pid, buf, conn, EINTR, Step::errormsg, pgNotify::extra, fprintf, gettimeofday(), i, Session::name, Step::name, TestSpec::nsessions, PG_DIAG_MESSAGE_PRIMARY, PG_DIAG_SEVERITY, pg_strdup(), PGRES_COMMAND_OK, PGRES_FATAL_ERROR, PGRES_TUPLES_OK, PQcancel(), PQclear(), PQconsumeInput(), PQerrorMessage(), PQexecPrepared(), PQfreeCancel(), PQfreemem(), PQgetCancel(), PQgetResult(), PQgetvalue(), PQisBusy(), PQnotifies(), PQntuples(), PQresStatus(), PQresultErrorField(), PQresultErrorMessage(), PQresultStatus(), PQsocket(), PREP_WAITING, printf, printResultSet(), psprintf(), pgNotify::relname, select, Step::session, TestSpec::sessions, snprintf, Step::sql, STEP_NONBLOCK, STEP_RETRY, strerror, USECS_PER_SEC, and waiting.

Referenced by run_permutation().

674 {
675  PGconn *conn = conns[1 + step->session];
676  fd_set read_set;
677  struct timeval start_time;
678  struct timeval timeout;
679  int sock = PQsocket(conn);
680  int ret;
681  PGresult *res;
682  PGnotify *notify;
683  bool canceled = false;
684 
685  if (sock < 0)
686  {
687  fprintf(stderr, "invalid socket: %s", PQerrorMessage(conn));
688  exit(1);
689  }
690 
691  gettimeofday(&start_time, NULL);
692  FD_ZERO(&read_set);
693 
694  while (PQisBusy(conn))
695  {
696  FD_SET(sock, &read_set);
697  timeout.tv_sec = 0;
698  timeout.tv_usec = 10000; /* Check for lock waits every 10ms. */
699 
700  ret = select(sock + 1, &read_set, NULL, NULL, &timeout);
701  if (ret < 0) /* error in select() */
702  {
703  if (errno == EINTR)
704  continue;
705  fprintf(stderr, "select failed: %s\n", strerror(errno));
706  exit(1);
707  }
708  else if (ret == 0) /* select() timeout: check for lock wait */
709  {
710  struct timeval current_time;
711  int64 td;
712 
713  /* If it's OK for the step to block, check whether it has. */
714  if (flags & STEP_NONBLOCK)
715  {
716  bool waiting;
717 
718  res = PQexecPrepared(conns[0], PREP_WAITING, 1,
719  &backend_pid_strs[step->session + 1],
720  NULL, NULL, 0);
721  if (PQresultStatus(res) != PGRES_TUPLES_OK ||
722  PQntuples(res) != 1)
723  {
724  fprintf(stderr, "lock wait query failed: %s",
725  PQerrorMessage(conns[0]));
726  exit(1);
727  }
728  waiting = ((PQgetvalue(res, 0, 0))[0] == 't');
729  PQclear(res);
730 
731  if (waiting) /* waiting to acquire a lock */
732  {
733  /*
734  * Since it takes time to perform the lock-check query,
735  * some data --- notably, NOTICE messages --- might have
736  * arrived since we looked. We must call PQconsumeInput
737  * and then PQisBusy to collect and process any such
738  * messages. In the (unlikely) case that PQisBusy then
739  * returns false, we might as well go examine the
740  * available result.
741  */
742  if (!PQconsumeInput(conn))
743  {
744  fprintf(stderr, "PQconsumeInput failed: %s\n",
745  PQerrorMessage(conn));
746  exit(1);
747  }
748  if (!PQisBusy(conn))
749  break;
750 
751  /*
752  * conn is still busy, so conclude that the step really is
753  * waiting.
754  */
755  if (!(flags & STEP_RETRY))
756  printf("step %s: %s <waiting ...>\n",
757  step->name, step->sql);
758  return true;
759  }
760  /* else, not waiting */
761  }
762 
763  /* Figure out how long we've been waiting for this step. */
764  gettimeofday(&current_time, NULL);
765  td = (int64) current_time.tv_sec - (int64) start_time.tv_sec;
766  td *= USECS_PER_SEC;
767  td += (int64) current_time.tv_usec - (int64) start_time.tv_usec;
768 
769  /*
770  * After 180 seconds, try to cancel the query.
771  *
772  * If the user tries to test an invalid permutation, we don't want
773  * to hang forever, especially when this is running in the
774  * buildfarm. This will presumably lead to this permutation
775  * failing, but remaining permutations and tests should still be
776  * OK.
777  */
778  if (td > 180 * USECS_PER_SEC && !canceled)
779  {
780  PGcancel *cancel = PQgetCancel(conn);
781 
782  if (cancel != NULL)
783  {
784  char buf[256];
785 
786  if (PQcancel(cancel, buf, sizeof(buf)))
787  canceled = true;
788  else
789  fprintf(stderr, "PQcancel failed: %s\n", buf);
790  PQfreeCancel(cancel);
791  }
792  }
793 
794  /*
795  * After 200 seconds, just give up and die.
796  *
797  * Since cleanup steps won't be run in this case, this may cause
798  * later tests to fail. That stinks, but it's better than waiting
799  * forever for the server to respond to the cancel.
800  */
801  if (td > 200 * USECS_PER_SEC)
802  {
803  fprintf(stderr, "step %s timed out after 200 seconds\n",
804  step->name);
805  exit(1);
806  }
807  }
808  else if (!PQconsumeInput(conn)) /* select(): data available */
809  {
810  fprintf(stderr, "PQconsumeInput failed: %s\n",
811  PQerrorMessage(conn));
812  exit(1);
813  }
814  }
815 
816  if (flags & STEP_RETRY)
817  printf("step %s: <... completed>\n", step->name);
818  else
819  printf("step %s: %s\n", step->name, step->sql);
820 
821  while ((res = PQgetResult(conn)))
822  {
823  switch (PQresultStatus(res))
824  {
825  case PGRES_COMMAND_OK:
826  break;
827  case PGRES_TUPLES_OK:
828  printResultSet(res);
829  break;
830  case PGRES_FATAL_ERROR:
831  if (step->errormsg != NULL)
832  {
833  printf("WARNING: this step had a leftover error message\n");
834  printf("%s\n", step->errormsg);
835  }
836 
837  /*
838  * Detail may contain XID values, so we want to just show
839  * primary. Beware however that libpq-generated error results
840  * may not contain subfields, only an old-style message.
841  */
842  {
843  const char *sev = PQresultErrorField(res,
845  const char *msg = PQresultErrorField(res,
847 
848  if (sev && msg)
849  step->errormsg = psprintf("%s: %s", sev, msg);
850  else
852  }
853  break;
854  default:
855  printf("unexpected result status: %s\n",
857  }
858  PQclear(res);
859  }
860 
861  /* Report any available NOTIFY messages, too */
862  PQconsumeInput(conn);
863  while ((notify = PQnotifies(conn)) != NULL)
864  {
865  /* Try to identify which session it came from */
866  const char *sendername = NULL;
867  char pidstring[32];
868 
869  for (int i = 0; i < testspec->nsessions; i++)
870  {
871  if (notify->be_pid == backend_pids[i + 1])
872  {
873  sendername = testspec->sessions[i]->name;
874  break;
875  }
876  }
877  if (sendername == NULL)
878  {
879  /* Doesn't seem to be any test session, so show the hard way */
880  snprintf(pidstring, sizeof(pidstring), "PID %d", notify->be_pid);
881  sendername = pidstring;
882  }
883  printf("%s: NOTIFY \"%s\" with payload \"%s\" from %s\n",
884  testspec->sessions[step->session]->name,
885  notify->relname, notify->extra, sendername);
886  PQfreemem(notify);
887  PQconsumeInput(conn);
888  }
889 
890  return false;
891 }
char * extra
Definition: libpq-fe.h:168
PGresult * PQexecPrepared(PGconn *conn, const char *stmtName, int nParams, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
Definition: fe-exec.c:2001
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6596
int gettimeofday(struct timeval *tp, struct timezone *tzp)
Definition: gettimeofday.c:105
#define PG_DIAG_MESSAGE_PRIMARY
Definition: postgres_ext.h:58
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3164
PGnotify * PQnotifies(PGconn *conn)
Definition: fe-exec.c:2290
#define USECS_PER_SEC
Definition: timestamp.h:94
Session ** sessions
void PQfreeCancel(PGcancel *cancel)
Definition: fe-connect.c:4242
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
char * PQresStatus(ExecStatusType status)
Definition: fe-exec.c:2701
#define printf(...)
Definition: port.h:198
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2770
#define fprintf
Definition: port.h:196
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:2693
static time_t start_time
Definition: pg_ctl.c:99
char * name
static PGconn ** conns
char * name
PGconn * conn
Definition: streamutil.c:56
int be_pid
Definition: libpq-fe.h:167
static int * backend_pids
static char * buf
Definition: pg_test_fsync.c:68
PGcancel * PQgetCancel(PGconn *conn)
Definition: fe-connect.c:4219
#define PG_DIAG_SEVERITY
Definition: postgres_ext.h:55
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
#define select(n, r, w, e, timeout)
Definition: win32_port.h:436
#define STEP_RETRY
char * relname
Definition: libpq-fe.h:166
char * errormsg
int PQconsumeInput(PGconn *conn)
Definition: fe-exec.c:1705
void PQclear(PGresult *res)
Definition: fe-exec.c:695
#define STEP_NONBLOCK
char * PQresultErrorField(const PGresult *res, int fieldcode)
Definition: fe-exec.c:2755
static const char ** backend_pid_strs
int PQisBusy(PGconn *conn)
Definition: fe-exec.c:1755
#define PREP_WAITING
#define strerror
Definition: port.h:205
char * sql
int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
Definition: fe-connect.c:4374
char * PQresultErrorMessage(const PGresult *res)
Definition: fe-exec.c:2709
int i
int session
#define EINTR
Definition: win32_port.h:323
#define snprintf
Definition: port.h:192
void PQfreemem(void *ptr)
Definition: fe-exec.c:3297
static volatile sig_atomic_t waiting
Definition: latch.c:123
int PQsocket(const PGconn *conn)
Definition: fe-connect.c:6614
PGresult * PQgetResult(PGconn *conn)
Definition: fe-exec.c:1779
static void printResultSet(PGresult *res)

Variable Documentation

◆ backend_pid_strs

const char** backend_pid_strs = NULL
static

Definition at line 31 of file isolationtester.c.

Referenced by main(), and try_complete_step().

◆ backend_pids

int* backend_pids = NULL
static

Definition at line 30 of file isolationtester.c.

Referenced by main(), and try_complete_step().

◆ conns

PGconn** conns = NULL
static

Definition at line 29 of file isolationtester.c.

Referenced by describeRoles().

◆ nconns

int nconns = 0
static

Definition at line 32 of file isolationtester.c.

Referenced by disconnect_atexit(), and main().

◆ piles

int* piles
static

Definition at line 229 of file isolationtester.c.

Referenced by run_all_permutations(), and run_all_permutations_recurse().