PostgreSQL Source Code  git master
isolationtester.c
Go to the documentation of this file.
1 /*
2  * src/test/isolation/isolationtester.c
3  *
4  * isolationtester.c
5  * Runs an isolation test specified by a spec file.
6  */
7 
8 #include "postgres_fe.h"
9 
10 #include <sys/time.h>
11 #ifdef HAVE_SYS_SELECT_H
12 #include <sys/select.h>
13 #endif
14 
15 #include "datatype/timestamp.h"
16 #include "isolationtester.h"
17 #include "libpq-fe.h"
18 #include "pg_getopt.h"
19 #include "pqexpbuffer.h"
20 
21 #define PREP_WAITING "isolationtester_waiting"
22 
23 /*
24  * conns[0] is the global setup, teardown, and watchdog connection. Additional
25  * connections represent spec-defined sessions. We also track the backend
26  * PID, in numeric and string formats, for each connection.
27  */
28 static PGconn **conns = NULL;
29 static int *backend_pids = NULL;
30 static const char **backend_pid_strs = NULL;
31 static int nconns = 0;
32 
33 /* Maximum time to wait before giving up on a step (in usec) */
34 static int64 max_step_wait = 300 * USECS_PER_SEC;
35 
36 
37 static void run_testspec(TestSpec *testspec);
38 static void run_all_permutations(TestSpec *testspec);
39 static void run_all_permutations_recurse(TestSpec *testspec, int nsteps,
40  Step **steps);
41 static void run_named_permutations(TestSpec *testspec);
42 static void run_permutation(TestSpec *testspec, int nsteps, Step **steps);
43 
44 #define STEP_NONBLOCK 0x1 /* return 0 as soon as cmd waits for a lock */
45 #define STEP_RETRY 0x2 /* this is a retry of a previously-waiting cmd */
46 static bool try_complete_step(TestSpec *testspec, Step *step, int flags);
47 
48 static int step_qsort_cmp(const void *a, const void *b);
49 static int step_bsearch_cmp(const void *a, const void *b);
50 
51 static void printResultSet(PGresult *res);
52 static void isotesterNoticeProcessor(void *arg, const char *message);
53 static void blackholeNoticeProcessor(void *arg, const char *message);
54 
55 static void
57 {
58  int i;
59 
60  for (i = 0; i < nconns; i++)
61  if (conns[i])
62  PQfinish(conns[i]);
63 }
64 
65 int
66 main(int argc, char **argv)
67 {
68  const char *conninfo;
69  const char *env_wait;
70  TestSpec *testspec;
71  int i,
72  j;
73  int n;
74  PGresult *res;
75  PQExpBufferData wait_query;
76  int opt;
77  int nallsteps;
78  Step **allsteps;
79 
80  while ((opt = getopt(argc, argv, "V")) != -1)
81  {
82  switch (opt)
83  {
84  case 'V':
85  puts("isolationtester (PostgreSQL) " PG_VERSION);
86  exit(0);
87  default:
88  fprintf(stderr, "Usage: isolationtester [CONNINFO]\n");
89  return EXIT_FAILURE;
90  }
91  }
92 
93  /*
94  * Make stdout unbuffered to match stderr; and ensure stderr is unbuffered
95  * too, which it should already be everywhere except sometimes in Windows.
96  */
97  setbuf(stdout, NULL);
98  setbuf(stderr, NULL);
99 
100  /*
101  * If the user supplies a non-option parameter on the command line, use it
102  * as the conninfo string; otherwise default to setting dbname=postgres
103  * and using environment variables or defaults for all other connection
104  * parameters.
105  */
106  if (argc > optind)
107  conninfo = argv[optind];
108  else
109  conninfo = "dbname = postgres";
110 
111  /*
112  * If PGISOLATIONTIMEOUT is set in the environment, adopt its value (given
113  * in seconds) as the max time to wait for any one step to complete.
114  */
115  env_wait = getenv("PGISOLATIONTIMEOUT");
116  if (env_wait != NULL)
117  max_step_wait = ((int64) atoi(env_wait)) * USECS_PER_SEC;
118 
119  /* Read the test spec from stdin */
120  spec_yyparse();
121  testspec = &parseresult;
122 
123  /* Create a lookup table of all steps. */
124  nallsteps = 0;
125  for (i = 0; i < testspec->nsessions; i++)
126  nallsteps += testspec->sessions[i]->nsteps;
127 
128  allsteps = pg_malloc(nallsteps * sizeof(Step *));
129 
130  n = 0;
131  for (i = 0; i < testspec->nsessions; i++)
132  {
133  for (j = 0; j < testspec->sessions[i]->nsteps; j++)
134  allsteps[n++] = testspec->sessions[i]->steps[j];
135  }
136 
137  qsort(allsteps, nallsteps, sizeof(Step *), &step_qsort_cmp);
138  testspec->nallsteps = nallsteps;
139  testspec->allsteps = allsteps;
140 
141  /* Verify that all step names are unique */
142  for (i = 1; i < testspec->nallsteps; i++)
143  {
144  if (strcmp(testspec->allsteps[i - 1]->name,
145  testspec->allsteps[i]->name) == 0)
146  {
147  fprintf(stderr, "duplicate step name: %s\n",
148  testspec->allsteps[i]->name);
149  exit(1);
150  }
151  }
152 
153  printf("Parsed test spec with %d sessions\n", testspec->nsessions);
154 
155  /*
156  * Establish connections to the database, one for each session and an
157  * extra for lock wait detection and global work.
158  */
159  nconns = 1 + testspec->nsessions;
160  conns = (PGconn **) pg_malloc0(nconns * sizeof(PGconn *));
163  atexit(disconnect_atexit);
164 
165  for (i = 0; i < nconns; i++)
166  {
167  conns[i] = PQconnectdb(conninfo);
168  if (PQstatus(conns[i]) != CONNECTION_OK)
169  {
170  fprintf(stderr, "Connection %d to database failed: %s",
171  i, PQerrorMessage(conns[i]));
172  exit(1);
173  }
174 
175  /*
176  * Set up notice processors for the user-defined connections, so that
177  * messages can get printed prefixed with the session names. The
178  * control connection gets a "blackhole" processor instead (hides all
179  * messages).
180  */
181  if (i != 0)
182  PQsetNoticeProcessor(conns[i],
184  (void *) (testspec->sessions[i - 1]->name));
185  else
186  PQsetNoticeProcessor(conns[i],
188  NULL);
189 
190  /* Save each connection's backend PID for subsequent use. */
191  backend_pids[i] = PQbackendPID(conns[i]);
193  }
194 
195  /* Set the session index fields in steps. */
196  for (i = 0; i < testspec->nsessions; i++)
197  {
198  Session *session = testspec->sessions[i];
199  int stepindex;
200 
201  for (stepindex = 0; stepindex < session->nsteps; stepindex++)
202  session->steps[stepindex]->session = i;
203  }
204 
205  /*
206  * Build the query we'll use to detect lock contention among sessions in
207  * the test specification. Most of the time, we could get away with
208  * simply checking whether a session is waiting for *any* lock: we don't
209  * exactly expect concurrent use of test tables. However, autovacuum will
210  * occasionally take AccessExclusiveLock to truncate a table, and we must
211  * ignore that transient wait.
212  */
213  initPQExpBuffer(&wait_query);
214  appendPQExpBufferStr(&wait_query,
215  "SELECT pg_catalog.pg_isolation_test_session_is_blocked($1, '{");
216  /* The spec syntax requires at least one session; assume that here. */
217  appendPQExpBufferStr(&wait_query, backend_pid_strs[1]);
218  for (i = 2; i < nconns; i++)
219  appendPQExpBuffer(&wait_query, ",%s", backend_pid_strs[i]);
220  appendPQExpBufferStr(&wait_query, "}')");
221 
222  res = PQprepare(conns[0], PREP_WAITING, wait_query.data, 0, NULL);
223  if (PQresultStatus(res) != PGRES_COMMAND_OK)
224  {
225  fprintf(stderr, "prepare of lock wait query failed: %s",
226  PQerrorMessage(conns[0]));
227  exit(1);
228  }
229  PQclear(res);
230  termPQExpBuffer(&wait_query);
231 
232  /*
233  * Run the permutations specified in the spec, or all if none were
234  * explicitly specified.
235  */
236  run_testspec(testspec);
237 
238  return 0;
239 }
240 
241 static int *piles;
242 
243 /*
244  * Run the permutations specified in the spec, or all if none were
245  * explicitly specified.
246  */
247 static void
249 {
250  int i;
251 
252  if (testspec->permutations)
253  run_named_permutations(testspec);
254  else
255  run_all_permutations(testspec);
256 
257  /*
258  * Verify that all steps have been used, complaining about anything
259  * defined but not used.
260  */
261  for (i = 0; i < testspec->nallsteps; i++)
262  {
263  if (!testspec->allsteps[i]->used)
264  fprintf(stderr, "unused step name: %s\n",
265  testspec->allsteps[i]->name);
266  }
267 }
268 
269 /*
270  * Run all permutations of the steps and sessions.
271  */
272 static void
274 {
275  int nsteps;
276  int i;
277  Step **steps;
278 
279  /* Count the total number of steps in all sessions */
280  nsteps = 0;
281  for (i = 0; i < testspec->nsessions; i++)
282  nsteps += testspec->sessions[i]->nsteps;
283 
284  steps = pg_malloc(sizeof(Step *) * nsteps);
285 
286  /*
287  * To generate the permutations, we conceptually put the steps of each
288  * session on a pile. To generate a permutation, we pick steps from the
289  * piles until all piles are empty. By picking steps from piles in
290  * different order, we get different permutations.
291  *
292  * A pile is actually just an integer which tells how many steps we've
293  * already picked from this pile.
294  */
295  piles = pg_malloc(sizeof(int) * testspec->nsessions);
296  for (i = 0; i < testspec->nsessions; i++)
297  piles[i] = 0;
298 
299  run_all_permutations_recurse(testspec, 0, steps);
300 }
301 
302 static void
303 run_all_permutations_recurse(TestSpec *testspec, int nsteps, Step **steps)
304 {
305  int i;
306  int found = 0;
307 
308  for (i = 0; i < testspec->nsessions; i++)
309  {
310  /* If there's any more steps in this pile, pick it and recurse */
311  if (piles[i] < testspec->sessions[i]->nsteps)
312  {
313  steps[nsteps] = testspec->sessions[i]->steps[piles[i]];
314  piles[i]++;
315 
316  run_all_permutations_recurse(testspec, nsteps + 1, steps);
317 
318  piles[i]--;
319 
320  found = 1;
321  }
322  }
323 
324  /* If all the piles were empty, this permutation is completed. Run it */
325  if (!found)
326  run_permutation(testspec, nsteps, steps);
327 }
328 
329 /*
330  * Run permutations given in the test spec
331  */
332 static void
334 {
335  int i,
336  j;
337 
338  for (i = 0; i < testspec->npermutations; i++)
339  {
340  Permutation *p = testspec->permutations[i];
341  Step **steps;
342 
343  steps = pg_malloc(p->nsteps * sizeof(Step *));
344 
345  /* Find all the named steps using the lookup table */
346  for (j = 0; j < p->nsteps; j++)
347  {
348  Step **this = (Step **) bsearch(p->stepnames[j],
349  testspec->allsteps,
350  testspec->nallsteps,
351  sizeof(Step *),
353 
354  if (this == NULL)
355  {
356  fprintf(stderr, "undefined step \"%s\" specified in permutation\n",
357  p->stepnames[j]);
358  exit(1);
359  }
360  steps[j] = *this;
361  }
362 
363  /* And run them */
364  run_permutation(testspec, p->nsteps, steps);
365 
366  free(steps);
367  }
368 }
369 
370 static int
371 step_qsort_cmp(const void *a, const void *b)
372 {
373  Step *stepa = *((Step **) a);
374  Step *stepb = *((Step **) b);
375 
376  return strcmp(stepa->name, stepb->name);
377 }
378 
379 static int
380 step_bsearch_cmp(const void *a, const void *b)
381 {
382  char *stepname = (char *) a;
383  Step *step = *((Step **) b);
384 
385  return strcmp(stepname, step->name);
386 }
387 
388 /*
389  * If a step caused an error to be reported, print it out and clear it.
390  */
391 static void
393 {
394  if (step->errormsg)
395  {
396  fprintf(stdout, "%s\n", step->errormsg);
397  free(step->errormsg);
398  step->errormsg = NULL;
399  }
400 }
401 
402 /*
403  * As above, but reports messages possibly emitted by multiple steps. This is
404  * useful when we have a blocked command awakened by another one; we want to
405  * report all messages identically, for the case where we don't care which
406  * one fails due to a timeout such as deadlock timeout.
407  */
408 static void
409 report_multiple_error_messages(Step *step, int nextra, Step **extrastep)
410 {
411  PQExpBufferData buffer;
412  int n;
413 
414  if (nextra == 0)
415  {
416  report_error_message(step);
417  return;
418  }
419 
420  initPQExpBuffer(&buffer);
421  appendPQExpBufferStr(&buffer, step->name);
422 
423  for (n = 0; n < nextra; ++n)
424  appendPQExpBuffer(&buffer, " %s", extrastep[n]->name);
425 
426  if (step->errormsg)
427  {
428  fprintf(stdout, "error in steps %s: %s\n", buffer.data,
429  step->errormsg);
430  free(step->errormsg);
431  step->errormsg = NULL;
432  }
433 
434  for (n = 0; n < nextra; ++n)
435  {
436  if (extrastep[n]->errormsg == NULL)
437  continue;
438  fprintf(stdout, "error in steps %s: %s\n",
439  buffer.data, extrastep[n]->errormsg);
440  free(extrastep[n]->errormsg);
441  extrastep[n]->errormsg = NULL;
442  }
443 
444  termPQExpBuffer(&buffer);
445 }
446 
447 /*
448  * Run one permutation
449  */
450 static void
451 run_permutation(TestSpec *testspec, int nsteps, Step **steps)
452 {
453  PGresult *res;
454  int i;
455  int w;
456  int nwaiting = 0;
457  int nerrorstep = 0;
458  Step **waiting;
459  Step **errorstep;
460 
461  waiting = pg_malloc(sizeof(Step *) * testspec->nsessions);
462  errorstep = pg_malloc(sizeof(Step *) * testspec->nsessions);
463 
464  printf("\nstarting permutation:");
465  for (i = 0; i < nsteps; i++)
466  {
467  /* Track the permutation as in-use */
468  steps[i]->used = true;
469  printf(" %s", steps[i]->name);
470  }
471  printf("\n");
472 
473  /* Perform setup */
474  for (i = 0; i < testspec->nsetupsqls; i++)
475  {
476  res = PQexec(conns[0], testspec->setupsqls[i]);
477  if (PQresultStatus(res) == PGRES_TUPLES_OK)
478  {
479  printResultSet(res);
480  }
481  else if (PQresultStatus(res) != PGRES_COMMAND_OK)
482  {
483  fprintf(stderr, "setup failed: %s", PQerrorMessage(conns[0]));
484  exit(1);
485  }
486  PQclear(res);
487  }
488 
489  /* Perform per-session setup */
490  for (i = 0; i < testspec->nsessions; i++)
491  {
492  if (testspec->sessions[i]->setupsql)
493  {
494  res = PQexec(conns[i + 1], testspec->sessions[i]->setupsql);
495  if (PQresultStatus(res) == PGRES_TUPLES_OK)
496  {
497  printResultSet(res);
498  }
499  else if (PQresultStatus(res) != PGRES_COMMAND_OK)
500  {
501  fprintf(stderr, "setup of session %s failed: %s",
502  testspec->sessions[i]->name,
503  PQerrorMessage(conns[i + 1]));
504  exit(1);
505  }
506  PQclear(res);
507  }
508  }
509 
510  /* Perform steps */
511  for (i = 0; i < nsteps; i++)
512  {
513  Step *step = steps[i];
514  PGconn *conn = conns[1 + step->session];
515  Step *oldstep = NULL;
516  bool mustwait;
517 
518  /*
519  * Check whether the session that needs to perform the next step is
520  * still blocked on an earlier step. If so, wait for it to finish.
521  *
522  * (In older versions of this tool, we allowed precisely one session
523  * to be waiting at a time. If we reached a step that required that
524  * session to execute the next command, we would declare the whole
525  * permutation invalid, cancel everything, and move on to the next
526  * one. Unfortunately, that made it impossible to test the deadlock
527  * detector using this framework, unless the number of processes
528  * involved in the deadlock was precisely two. We now assume that if
529  * we reach a step that is still blocked, we need to wait for it to
530  * unblock itself.)
531  */
532  for (w = 0; w < nwaiting; ++w)
533  {
534  if (step->session == waiting[w]->session)
535  {
536  oldstep = waiting[w];
537 
538  /* Wait for previous step on this connection. */
539  try_complete_step(testspec, oldstep, STEP_RETRY);
540 
541  /* Remove that step from the waiting[] array. */
542  if (w + 1 < nwaiting)
543  memmove(&waiting[w], &waiting[w + 1],
544  (nwaiting - (w + 1)) * sizeof(Step *));
545  nwaiting--;
546 
547  break;
548  }
549  }
550  if (oldstep != NULL)
551  {
552  /*
553  * Check for completion of any steps that were previously waiting.
554  * Remove any that have completed from waiting[], and include them
555  * in the list for report_multiple_error_messages().
556  */
557  w = 0;
558  nerrorstep = 0;
559  while (w < nwaiting)
560  {
561  if (try_complete_step(testspec, waiting[w],
563  {
564  /* Still blocked on a lock, leave it alone. */
565  w++;
566  }
567  else
568  {
569  /* This one finished, too! */
570  errorstep[nerrorstep++] = waiting[w];
571  if (w + 1 < nwaiting)
572  memmove(&waiting[w], &waiting[w + 1],
573  (nwaiting - (w + 1)) * sizeof(Step *));
574  nwaiting--;
575  }
576  }
577 
578  /* Report all errors together. */
579  report_multiple_error_messages(oldstep, nerrorstep, errorstep);
580  }
581 
582  /* Send the query for this step. */
583  if (!PQsendQuery(conn, step->sql))
584  {
585  fprintf(stdout, "failed to send query for step %s: %s\n",
586  step->name, PQerrorMessage(conn));
587  exit(1);
588  }
589 
590  /* Try to complete this step without blocking. */
591  mustwait = try_complete_step(testspec, step, STEP_NONBLOCK);
592 
593  /* Check for completion of any steps that were previously waiting. */
594  w = 0;
595  nerrorstep = 0;
596  while (w < nwaiting)
597  {
598  if (try_complete_step(testspec, waiting[w],
600  w++;
601  else
602  {
603  errorstep[nerrorstep++] = waiting[w];
604  if (w + 1 < nwaiting)
605  memmove(&waiting[w], &waiting[w + 1],
606  (nwaiting - (w + 1)) * sizeof(Step *));
607  nwaiting--;
608  }
609  }
610 
611  /* Report any error from this step, and any steps that it unblocked. */
612  report_multiple_error_messages(step, nerrorstep, errorstep);
613 
614  /* If this step is waiting, add it to the array of waiters. */
615  if (mustwait)
616  waiting[nwaiting++] = step;
617  }
618 
619  /* Wait for any remaining queries. */
620  for (w = 0; w < nwaiting; ++w)
621  {
622  try_complete_step(testspec, waiting[w], STEP_RETRY);
623  report_error_message(waiting[w]);
624  }
625 
626  /* Perform per-session teardown */
627  for (i = 0; i < testspec->nsessions; i++)
628  {
629  if (testspec->sessions[i]->teardownsql)
630  {
631  res = PQexec(conns[i + 1], testspec->sessions[i]->teardownsql);
632  if (PQresultStatus(res) == PGRES_TUPLES_OK)
633  {
634  printResultSet(res);
635  }
636  else if (PQresultStatus(res) != PGRES_COMMAND_OK)
637  {
638  fprintf(stderr, "teardown of session %s failed: %s",
639  testspec->sessions[i]->name,
640  PQerrorMessage(conns[i + 1]));
641  /* don't exit on teardown failure */
642  }
643  PQclear(res);
644  }
645  }
646 
647  /* Perform teardown */
648  if (testspec->teardownsql)
649  {
650  res = PQexec(conns[0], testspec->teardownsql);
651  if (PQresultStatus(res) == PGRES_TUPLES_OK)
652  {
653  printResultSet(res);
654  }
655  else if (PQresultStatus(res) != PGRES_COMMAND_OK)
656  {
657  fprintf(stderr, "teardown failed: %s",
658  PQerrorMessage(conns[0]));
659  /* don't exit on teardown failure */
660  }
661  PQclear(res);
662  }
663 
664  free(waiting);
665  free(errorstep);
666 }
667 
668 /*
669  * Our caller already sent the query associated with this step. Wait for it
670  * to either complete or (if given the STEP_NONBLOCK flag) to block while
671  * waiting for a lock. We assume that any lock wait will persist until we
672  * have executed additional steps in the permutation.
673  *
674  * When calling this function on behalf of a given step for a second or later
675  * time, pass the STEP_RETRY flag. This only affects the messages printed.
676  *
677  * If the query returns an error, the message is saved in step->errormsg.
678  * Caller should call report_error_message shortly after this, to have it
679  * printed and cleared.
680  *
681  * If the STEP_NONBLOCK flag was specified and the query is waiting to acquire
682  * a lock, returns true. Otherwise, returns false.
683  */
684 static bool
685 try_complete_step(TestSpec *testspec, Step *step, int flags)
686 {
687  PGconn *conn = conns[1 + step->session];
688  fd_set read_set;
689  struct timeval start_time;
690  struct timeval timeout;
691  int sock = PQsocket(conn);
692  int ret;
693  PGresult *res;
694  PGnotify *notify;
695  bool canceled = false;
696 
697  if (sock < 0)
698  {
699  fprintf(stderr, "invalid socket: %s", PQerrorMessage(conn));
700  exit(1);
701  }
702 
703  gettimeofday(&start_time, NULL);
704  FD_ZERO(&read_set);
705 
706  while (PQisBusy(conn))
707  {
708  FD_SET(sock, &read_set);
709  timeout.tv_sec = 0;
710  timeout.tv_usec = 10000; /* Check for lock waits every 10ms. */
711 
712  ret = select(sock + 1, &read_set, NULL, NULL, &timeout);
713  if (ret < 0) /* error in select() */
714  {
715  if (errno == EINTR)
716  continue;
717  fprintf(stderr, "select failed: %s\n", strerror(errno));
718  exit(1);
719  }
720  else if (ret == 0) /* select() timeout: check for lock wait */
721  {
722  struct timeval current_time;
723  int64 td;
724 
725  /* If it's OK for the step to block, check whether it has. */
726  if (flags & STEP_NONBLOCK)
727  {
728  bool waiting;
729 
730  res = PQexecPrepared(conns[0], PREP_WAITING, 1,
731  &backend_pid_strs[step->session + 1],
732  NULL, NULL, 0);
733  if (PQresultStatus(res) != PGRES_TUPLES_OK ||
734  PQntuples(res) != 1)
735  {
736  fprintf(stderr, "lock wait query failed: %s",
737  PQerrorMessage(conns[0]));
738  exit(1);
739  }
740  waiting = ((PQgetvalue(res, 0, 0))[0] == 't');
741  PQclear(res);
742 
743  if (waiting) /* waiting to acquire a lock */
744  {
745  /*
746  * Since it takes time to perform the lock-check query,
747  * some data --- notably, NOTICE messages --- might have
748  * arrived since we looked. We must call PQconsumeInput
749  * and then PQisBusy to collect and process any such
750  * messages. In the (unlikely) case that PQisBusy then
751  * returns false, we might as well go examine the
752  * available result.
753  */
754  if (!PQconsumeInput(conn))
755  {
756  fprintf(stderr, "PQconsumeInput failed: %s\n",
757  PQerrorMessage(conn));
758  exit(1);
759  }
760  if (!PQisBusy(conn))
761  break;
762 
763  /*
764  * conn is still busy, so conclude that the step really is
765  * waiting.
766  */
767  if (!(flags & STEP_RETRY))
768  printf("step %s: %s <waiting ...>\n",
769  step->name, step->sql);
770  return true;
771  }
772  /* else, not waiting */
773  }
774 
775  /* Figure out how long we've been waiting for this step. */
776  gettimeofday(&current_time, NULL);
777  td = (int64) current_time.tv_sec - (int64) start_time.tv_sec;
778  td *= USECS_PER_SEC;
779  td += (int64) current_time.tv_usec - (int64) start_time.tv_usec;
780 
781  /*
782  * After max_step_wait microseconds, try to cancel the query.
783  *
784  * If the user tries to test an invalid permutation, we don't want
785  * to hang forever, especially when this is running in the
786  * buildfarm. This will presumably lead to this permutation
787  * failing, but remaining permutations and tests should still be
788  * OK.
789  */
790  if (td > max_step_wait && !canceled)
791  {
792  PGcancel *cancel = PQgetCancel(conn);
793 
794  if (cancel != NULL)
795  {
796  char buf[256];
797 
798  if (PQcancel(cancel, buf, sizeof(buf)))
799  {
800  /*
801  * print to stdout not stderr, as this should appear
802  * in the test case's results
803  */
804  printf("isolationtester: canceling step %s after %d seconds\n",
805  step->name, (int) (td / USECS_PER_SEC));
806  canceled = true;
807  }
808  else
809  fprintf(stderr, "PQcancel failed: %s\n", buf);
810  PQfreeCancel(cancel);
811  }
812  }
813 
814  /*
815  * After twice max_step_wait, just give up and die.
816  *
817  * Since cleanup steps won't be run in this case, this may cause
818  * later tests to fail. That stinks, but it's better than waiting
819  * forever for the server to respond to the cancel.
820  */
821  if (td > 2 * max_step_wait)
822  {
823  fprintf(stderr, "step %s timed out after %d seconds\n",
824  step->name, (int) (td / USECS_PER_SEC));
825  exit(1);
826  }
827  }
828  else if (!PQconsumeInput(conn)) /* select(): data available */
829  {
830  fprintf(stderr, "PQconsumeInput failed: %s\n",
831  PQerrorMessage(conn));
832  exit(1);
833  }
834  }
835 
836  if (flags & STEP_RETRY)
837  printf("step %s: <... completed>\n", step->name);
838  else
839  printf("step %s: %s\n", step->name, step->sql);
840 
841  while ((res = PQgetResult(conn)))
842  {
843  switch (PQresultStatus(res))
844  {
845  case PGRES_COMMAND_OK:
846  break;
847  case PGRES_TUPLES_OK:
848  printResultSet(res);
849  break;
850  case PGRES_FATAL_ERROR:
851  if (step->errormsg != NULL)
852  {
853  printf("WARNING: this step had a leftover error message\n");
854  printf("%s\n", step->errormsg);
855  }
856 
857  /*
858  * Detail may contain XID values, so we want to just show
859  * primary. Beware however that libpq-generated error results
860  * may not contain subfields, only an old-style message.
861  */
862  {
863  const char *sev = PQresultErrorField(res,
865  const char *msg = PQresultErrorField(res,
867 
868  if (sev && msg)
869  step->errormsg = psprintf("%s: %s", sev, msg);
870  else
872  }
873  break;
874  default:
875  printf("unexpected result status: %s\n",
877  }
878  PQclear(res);
879  }
880 
881  /* Report any available NOTIFY messages, too */
882  PQconsumeInput(conn);
883  while ((notify = PQnotifies(conn)) != NULL)
884  {
885  /* Try to identify which session it came from */
886  const char *sendername = NULL;
887  char pidstring[32];
888 
889  for (int i = 0; i < testspec->nsessions; i++)
890  {
891  if (notify->be_pid == backend_pids[i + 1])
892  {
893  sendername = testspec->sessions[i]->name;
894  break;
895  }
896  }
897  if (sendername == NULL)
898  {
899  /* Doesn't seem to be any test session, so show the hard way */
900  snprintf(pidstring, sizeof(pidstring), "PID %d", notify->be_pid);
901  sendername = pidstring;
902  }
903  printf("%s: NOTIFY \"%s\" with payload \"%s\" from %s\n",
904  testspec->sessions[step->session]->name,
905  notify->relname, notify->extra, sendername);
906  PQfreemem(notify);
907  PQconsumeInput(conn);
908  }
909 
910  return false;
911 }
912 
913 static void
915 {
916  int nFields;
917  int i,
918  j;
919 
920  /* first, print out the attribute names */
921  nFields = PQnfields(res);
922  for (i = 0; i < nFields; i++)
923  printf("%-15s", PQfname(res, i));
924  printf("\n\n");
925 
926  /* next, print out the rows */
927  for (i = 0; i < PQntuples(res); i++)
928  {
929  for (j = 0; j < nFields; j++)
930  printf("%-15s", PQgetvalue(res, i, j));
931  printf("\n");
932  }
933 }
934 
935 /* notice processor, prefixes each message with the session name */
936 static void
937 isotesterNoticeProcessor(void *arg, const char *message)
938 {
939  printf("%s: %s", (char *) arg, message);
940 }
941 
942 /* notice processor, hides the message */
943 static void
944 blackholeNoticeProcessor(void *arg, const char *message)
945 {
946  /* do nothing */
947 }
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:2000
int PQnfields(const PGresult *res)
Definition: fe-exec.c:2777
PGresult * PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
Definition: fe-exec.c:1983
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6623
int gettimeofday(struct timeval *tp, struct timezone *tzp)
Definition: gettimeofday.c:105
static int64 max_step_wait
#define PG_DIAG_MESSAGE_PRIMARY
Definition: postgres_ext.h:58
PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
Definition: fe-connect.c:6799
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3163
PGnotify * PQnotifies(PGconn *conn)
Definition: fe-exec.c:2289
char * PQfname(const PGresult *res, int field_num)
Definition: fe-exec.c:2855
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
#define USECS_PER_SEC
Definition: timestamp.h:94
static void run_named_permutations(TestSpec *testspec)
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
Session ** sessions
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
char ** setupsqls
bool used
void PQfreeCancel(PGcancel *cancel)
Definition: fe-connect.c:4269
int main(int argc, char **argv)
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
static void run_all_permutations(TestSpec *testspec)
char * teardownsql
static void report_multiple_error_messages(Step *step, int nextra, Step **extrastep)
char * PQresStatus(ExecStatusType status)
Definition: fe-exec.c:2700
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4125
#define printf(...)
Definition: port.h:198
Permutation ** permutations
char * setupsql
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2769
#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:2692
char * name
int npermutations
static void blackholeNoticeProcessor(void *arg, const char *message)
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1234
int optind
Definition: getopt.c:50
static PGconn ** conns
static bool try_complete_step(TestSpec *testspec, Step *step, int flags)
char * name
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
char * teardownsql
PGconn * conn
Definition: streamutil.c:54
int be_pid
Definition: libpq-fe.h:167
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
static int * backend_pids
static int step_bsearch_cmp(const void *a, const void *b)
static void disconnect_atexit(void)
static char * buf
Definition: pg_test_fsync.c:67
#define memmove(d, s, c)
Definition: c.h:1267
PGcancel * PQgetCancel(PGconn *conn)
Definition: fe-connect.c:4246
#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 ** stepnames
char * relname
Definition: libpq-fe.h:166
static int * piles
char * errormsg
int PQbackendPID(const PGconn *conn)
Definition: fe-connect.c:6649
int PQconsumeInput(PGconn *conn)
Definition: fe-exec.c:1704
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#define STEP_NONBLOCK
#define free(a)
Definition: header.h:65
char * PQresultErrorField(const PGresult *res, int fieldcode)
Definition: fe-exec.c:2754
static const char ** backend_pid_strs
static void run_permutation(TestSpec *testspec, int nsteps, Step **steps)
static void run_all_permutations_recurse(TestSpec *testspec, int nsteps, Step **steps)
int PQisBusy(PGconn *conn)
Definition: fe-exec.c:1754
static void isotesterNoticeProcessor(void *arg, const char *message)
#define PREP_WAITING
#define strerror
Definition: port.h:205
Step ** steps
char * sql
const char * name
Definition: encode.c:521
static void report_error_message(Step *step)
static void run_testspec(TestSpec *testspec)
int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
Definition: fe-connect.c:4401
char * PQresultErrorMessage(const PGresult *res)
Definition: fe-exec.c:2708
int i
Step ** allsteps
#define EXIT_FAILURE
Definition: settings.h:153
PGresult * PQexec(PGconn *conn, const char *query)
Definition: fe-exec.c:1939
void * arg
int session
TestSpec parseresult
#define EINTR
Definition: win32_port.h:323
#define qsort(a, b, c, d)
Definition: port.h:488
ConnStatusType PQstatus(const PGconn *conn)
Definition: fe-connect.c:6570
static int nconns
#define snprintf
Definition: port.h:192
int spec_yyparse(void)
void PQfreemem(void *ptr)
Definition: fe-exec.c:3296
static volatile sig_atomic_t waiting
Definition: latch.c:123
int PQsocket(const PGconn *conn)
Definition: fe-connect.c:6641
PGresult * PQgetResult(PGconn *conn)
Definition: fe-exec.c:1778
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92
static void printResultSet(PGresult *res)
PGconn * PQconnectdb(const char *conninfo)
Definition: fe-connect.c:680