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

Go to the source code of this file.

Data Structures

struct  IsoConnInfo
 

Macros

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

Typedefs

typedef struct IsoConnInfo IsoConnInfo
 

Functions

static void check_testspec (TestSpec *testspec)
 
static void run_testspec (TestSpec *testspec)
 
static void run_all_permutations (TestSpec *testspec)
 
static void run_all_permutations_recurse (TestSpec *testspec, int *piles, int nsteps, PermutationStep **steps)
 
static void run_named_permutations (TestSpec *testspec)
 
static void run_permutation (TestSpec *testspec, int nsteps, PermutationStep **steps)
 
static int try_complete_steps (TestSpec *testspec, PermutationStep **waiting, int nwaiting, int flags)
 
static bool try_complete_step (TestSpec *testspec, PermutationStep *pstep, 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 bool step_has_blocker (PermutationStep *pstep)
 
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)
 

Variables

static IsoConnInfoconns = NULL
 
static int nconns = 0
 
static bool any_new_notice = false
 
static int64 max_step_wait = 360 * USECS_PER_SEC
 

Macro Definition Documentation

◆ PREP_WAITING

#define PREP_WAITING   "isolationtester_waiting"

Definition at line 19 of file isolationtester.c.

◆ STEP_NONBLOCK

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

Definition at line 60 of file isolationtester.c.

◆ STEP_RETRY

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

Definition at line 61 of file isolationtester.c.

Typedef Documentation

◆ IsoConnInfo

typedef struct IsoConnInfo IsoConnInfo

Function Documentation

◆ blackholeNoticeProcessor()

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

Definition at line 1139 of file isolationtester.c.

1140{
1141 /* do nothing */
1142}

Referenced by main().

◆ check_testspec()

static void check_testspec ( TestSpec testspec)
static

Definition at line 252 of file isolationtester.c.

253{
254 int nallsteps;
255 Step **allsteps;
256 int i,
257 j,
258 k;
259
260 /* Create a sorted lookup table of all steps. */
261 nallsteps = 0;
262 for (i = 0; i < testspec->nsessions; i++)
263 nallsteps += testspec->sessions[i]->nsteps;
264
265 allsteps = pg_malloc(nallsteps * sizeof(Step *));
266
267 k = 0;
268 for (i = 0; i < testspec->nsessions; i++)
269 {
270 for (j = 0; j < testspec->sessions[i]->nsteps; j++)
271 allsteps[k++] = testspec->sessions[i]->steps[j];
272 }
273
274 qsort(allsteps, nallsteps, sizeof(Step *), step_qsort_cmp);
275
276 /* Verify that all step names are unique. */
277 for (i = 1; i < nallsteps; i++)
278 {
279 if (strcmp(allsteps[i - 1]->name,
280 allsteps[i]->name) == 0)
281 {
282 fprintf(stderr, "duplicate step name: %s\n",
283 allsteps[i]->name);
284 exit(1);
285 }
286 }
287
288 /* Set the session index fields in steps. */
289 for (i = 0; i < testspec->nsessions; i++)
290 {
291 Session *session = testspec->sessions[i];
292
293 for (j = 0; j < session->nsteps; j++)
294 session->steps[j]->session = i;
295 }
296
297 /*
298 * If we have manually-specified permutations, link PermutationSteps to
299 * Steps, and fill in blocker links.
300 */
301 for (i = 0; i < testspec->npermutations; i++)
302 {
303 Permutation *p = testspec->permutations[i];
304
305 for (j = 0; j < p->nsteps; j++)
306 {
307 PermutationStep *pstep = p->steps[j];
308 Step **this = (Step **) bsearch(pstep->name,
309 allsteps,
310 nallsteps,
311 sizeof(Step *),
313
314 if (this == NULL)
315 {
316 fprintf(stderr, "undefined step \"%s\" specified in permutation\n",
317 pstep->name);
318 exit(1);
319 }
320 pstep->step = *this;
321
322 /* Mark the step used, for check below */
323 pstep->step->used = true;
324 }
325
326 /*
327 * Identify any blocker steps. We search only the current
328 * permutation, since steps not used there couldn't be concurrent.
329 * Note that it's OK to reference later permutation steps, so this
330 * can't be combined with the previous loop.
331 */
332 for (j = 0; j < p->nsteps; j++)
333 {
334 PermutationStep *pstep = p->steps[j];
335
336 for (k = 0; k < pstep->nblockers; k++)
337 {
338 PermutationStepBlocker *blocker = pstep->blockers[k];
339 int n;
340
341 if (blocker->blocktype == PSB_ONCE)
342 continue; /* nothing to link to */
343
344 blocker->step = NULL;
345 for (n = 0; n < p->nsteps; n++)
346 {
347 PermutationStep *otherp = p->steps[n];
348
349 if (strcmp(otherp->name, blocker->stepname) == 0)
350 {
351 blocker->step = otherp->step;
352 break;
353 }
354 }
355 if (blocker->step == NULL)
356 {
357 fprintf(stderr, "undefined blocking step \"%s\" referenced in permutation step \"%s\"\n",
358 blocker->stepname, pstep->name);
359 exit(1);
360 }
361 /* can't block on completion of step of own session */
362 if (blocker->step->session == pstep->step->session)
363 {
364 fprintf(stderr, "permutation step \"%s\" cannot block on its own session\n",
365 pstep->name);
366 exit(1);
367 }
368 }
369 }
370 }
371
372 /*
373 * If we have manually-specified permutations, verify that all steps have
374 * been used, warning about anything defined but not used. We can skip
375 * this when using automatically-generated permutations.
376 */
377 if (testspec->permutations)
378 {
379 for (i = 0; i < nallsteps; i++)
380 {
381 if (!allsteps[i]->used)
382 fprintf(stderr, "unused step name: %s\n", allsteps[i]->name);
383 }
384 }
385
386 free(allsteps);
387}
#define fprintf(file, fmt, msg)
Definition: cubescan.l:21
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
#define free(a)
Definition: header.h:65
int j
Definition: isn.c:73
int i
Definition: isn.c:72
static int step_bsearch_cmp(const void *a, const void *b)
static int step_qsort_cmp(const void *a, const void *b)
@ PSB_ONCE
exit(1)
#define qsort(a, b, c, d)
Definition: port.h:474
PermutationStepBlockerType blocktype
PermutationStepBlocker ** blockers
PermutationStep ** steps
Step ** steps
int session
bool used
Session ** sessions
Permutation ** permutations
int npermutations
const char * name

References PermutationStep::blockers, PermutationStepBlocker::blocktype, exit(), fprintf, free, i, j, name, PermutationStep::name, PermutationStep::nblockers, TestSpec::npermutations, TestSpec::nsessions, Session::nsteps, Permutation::nsteps, TestSpec::permutations, pg_malloc(), PSB_ONCE, qsort, Step::session, TestSpec::sessions, PermutationStepBlocker::step, PermutationStep::step, step_bsearch_cmp(), step_qsort_cmp(), PermutationStepBlocker::stepname, Session::steps, Permutation::steps, and Step::used.

Referenced by main().

◆ disconnect_atexit()

static void disconnect_atexit ( void  )
static

Definition at line 77 of file isolationtester.c.

78{
79 int i;
80
81 for (i = 0; i < nconns; i++)
82 if (conns[i].conn)
84}
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4939
static IsoConnInfo * conns
static int nconns
PGconn * conn
Definition: streamutil.c:53

References conn, conns, i, nconns, and PQfinish().

Referenced by main().

◆ isotesterNoticeProcessor()

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

Definition at line 1126 of file isolationtester.c.

1127{
1128 IsoConnInfo *myconn = (IsoConnInfo *) arg;
1129
1130 /* Prefix the backend's message with the session name. */
1131 printf("%s: %s", myconn->sessionname, message);
1132 /* Record notices, since we may need this to decide to unblock a step. */
1133 myconn->total_notices++;
1134 any_new_notice = true;
1135}
static bool any_new_notice
void * arg
#define printf(...)
Definition: port.h:244
const char * sessionname

References any_new_notice, arg, printf, IsoConnInfo::sessionname, and IsoConnInfo::total_notices.

Referenced by main().

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 87 of file isolationtester.c.

88{
89 const char *conninfo;
90 const char *env_wait;
91 TestSpec *testspec;
93 PQExpBufferData wait_query;
94 int opt;
95 int i;
96
97 while ((opt = getopt(argc, argv, "V")) != -1)
98 {
99 switch (opt)
100 {
101 case 'V':
102 puts("isolationtester (PostgreSQL) " PG_VERSION);
103 exit(0);
104 default:
105 fprintf(stderr, "Usage: isolationtester [CONNINFO]\n");
106 return EXIT_FAILURE;
107 }
108 }
109
110 /*
111 * Make stdout unbuffered to match stderr; and ensure stderr is unbuffered
112 * too, which it should already be everywhere except sometimes in Windows.
113 */
114 setbuf(stdout, NULL);
115 setbuf(stderr, NULL);
116
117 /*
118 * If the user supplies a non-option parameter on the command line, use it
119 * as the conninfo string; otherwise default to setting dbname=postgres
120 * and using environment variables or defaults for all other connection
121 * parameters.
122 */
123 if (argc > optind)
124 conninfo = argv[optind];
125 else
126 conninfo = "dbname = postgres";
127
128 /*
129 * If PG_TEST_TIMEOUT_DEFAULT is set, adopt its value (given in seconds)
130 * as half the max time to wait for any one step to complete.
131 */
132 env_wait = getenv("PG_TEST_TIMEOUT_DEFAULT");
133 if (env_wait != NULL)
134 max_step_wait = 2 * ((int64) atoi(env_wait)) * USECS_PER_SEC;
135
136 /* Read the test spec from stdin */
137 spec_yyparse();
138 testspec = &parseresult;
139
140 /* Perform post-parse checking, and fill in linking fields */
141 check_testspec(testspec);
142
143 printf("Parsed test spec with %d sessions\n", testspec->nsessions);
144
145 /*
146 * Establish connections to the database, one for each session and an
147 * extra for lock wait detection and global work.
148 */
149 nconns = 1 + testspec->nsessions;
151 atexit(disconnect_atexit);
152
153 for (i = 0; i < nconns; i++)
154 {
155 const char *sessionname;
156
157 if (i == 0)
158 sessionname = "control connection";
159 else
160 sessionname = testspec->sessions[i - 1]->name;
161
162 conns[i].sessionname = sessionname;
163
164 conns[i].conn = PQconnectdb(conninfo);
166 {
167 fprintf(stderr, "Connection %d failed: %s",
169 exit(1);
170 }
171
172 /*
173 * Set up notice processors for the user-defined connections, so that
174 * messages can get printed prefixed with the session names. The
175 * control connection gets a "blackhole" processor instead (hides all
176 * messages).
177 */
178 if (i != 0)
181 &conns[i]);
182 else
185 NULL);
186
187 /*
188 * Similarly, append the session name to application_name to make it
189 * easier to map spec file sessions to log output and
190 * pg_stat_activity. The reason to append instead of just setting the
191 * name is that we don't know the name of the test currently running.
192 */
194 "SELECT set_config('application_name',\n"
195 " current_setting('application_name') || '/' || $1,\n"
196 " false)",
197 1, NULL,
198 &sessionname,
199 NULL, NULL, 0);
201 {
202 fprintf(stderr, "setting of application name failed: %s",
204 exit(1);
205 }
206
207 /* Save each connection's backend PID for subsequent use. */
209 conns[i].backend_pid_str = psprintf("%d", conns[i].backend_pid);
210 }
211
212 /*
213 * Build the query we'll use to detect lock contention among sessions in
214 * the test specification. Most of the time, we could get away with
215 * simply checking whether a session is waiting for *any* lock: we don't
216 * exactly expect concurrent use of test tables. However, autovacuum will
217 * occasionally take AccessExclusiveLock to truncate a table, and we must
218 * ignore that transient wait.
219 */
220 initPQExpBuffer(&wait_query);
221 appendPQExpBufferStr(&wait_query,
222 "SELECT pg_catalog.pg_isolation_test_session_is_blocked($1, '{");
223 /* The spec syntax requires at least one session; assume that here. */
224 appendPQExpBufferStr(&wait_query, conns[1].backend_pid_str);
225 for (i = 2; i < nconns; i++)
226 appendPQExpBuffer(&wait_query, ",%s", conns[i].backend_pid_str);
227 appendPQExpBufferStr(&wait_query, "}')");
228
229 res = PQprepare(conns[0].conn, PREP_WAITING, wait_query.data, 0, NULL);
231 {
232 fprintf(stderr, "prepare of lock wait query failed: %s",
234 exit(1);
235 }
236 PQclear(res);
237 termPQExpBuffer(&wait_query);
238
239 /*
240 * Run the permutations specified in the spec, or all if none were
241 * explicitly specified.
242 */
243 run_testspec(testspec);
244
245 return 0;
246}
int64_t int64
Definition: c.h:485
#define USECS_PER_SEC
Definition: timestamp.h:134
PGconn * PQconnectdb(const char *conninfo)
Definition: fe-connect.c:753
ConnStatusType PQstatus(const PGconn *conn)
Definition: fe-connect.c:7205
int PQbackendPID(const PGconn *conn)
Definition: fe-connect.c:7302
PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
Definition: fe-connect.c:7447
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7268
PGresult * PQexecParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
Definition: fe-exec.c:2276
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3411
PGresult * PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
Definition: fe-exec.c:2306
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
static void run_testspec(TestSpec *testspec)
static void blackholeNoticeProcessor(void *arg, const char *message)
static void check_testspec(TestSpec *testspec)
static void isotesterNoticeProcessor(void *arg, const char *message)
static int64 max_step_wait
#define PREP_WAITING
static void disconnect_atexit(void)
TestSpec parseresult
int spec_yyparse(void)
@ CONNECTION_OK
Definition: libpq-fe.h:81
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:120
@ PGRES_TUPLES_OK
Definition: libpq-fe.h:123
PGDLLIMPORT int optind
Definition: getopt.c:51
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition: getopt.c:72
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:90
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:129
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
#define EXIT_FAILURE
Definition: settings.h:178
const char * backend_pid_str
PGconn * conn
char * name

References appendPQExpBuffer(), appendPQExpBufferStr(), IsoConnInfo::backend_pid, IsoConnInfo::backend_pid_str, blackholeNoticeProcessor(), check_testspec(), conn, IsoConnInfo::conn, CONNECTION_OK, conns, PQExpBufferData::data, disconnect_atexit(), exit(), EXIT_FAILURE, fprintf, getopt(), i, initPQExpBuffer(), isotesterNoticeProcessor(), max_step_wait, Session::name, nconns, TestSpec::nsessions, optind, parseresult, pg_malloc0(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQbackendPID(), PQclear(), PQconnectdb(), PQerrorMessage(), PQexecParams(), PQprepare(), PQresultStatus(), PQsetNoticeProcessor(), PQstatus(), PREP_WAITING, printf, psprintf(), res, run_testspec(), IsoConnInfo::sessionname, TestSpec::sessions, spec_yyparse(), generate_unaccent_rules::stdout, termPQExpBuffer(), and USECS_PER_SEC.

◆ printResultSet()

static void printResultSet ( PGresult res)
static

Definition at line 1113 of file isolationtester.c.

1114{
1115 PQprintOpt popt;
1116
1117 memset(&popt, 0, sizeof(popt));
1118 popt.header = true;
1119 popt.align = true;
1120 popt.fieldSep = "|";
1121 PQprint(stdout, res, &popt);
1122}
void PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
Definition: fe-print.c:68
pqbool align
Definition: libpq-fe.h:238
pqbool header
Definition: libpq-fe.h:237
char * fieldSep
Definition: libpq-fe.h:243

References _PQprintOpt::align, _PQprintOpt::fieldSep, _PQprintOpt::header, PQprint(), res, and generate_unaccent_rules::stdout.

Referenced by run_permutation(), and try_complete_step().

◆ run_all_permutations()

static void run_all_permutations ( TestSpec testspec)
static

Definition at line 406 of file isolationtester.c.

407{
408 int nsteps;
409 int i;
410 PermutationStep *steps;
411 PermutationStep **stepptrs;
412 int *piles;
413
414 /* Count the total number of steps in all sessions */
415 nsteps = 0;
416 for (i = 0; i < testspec->nsessions; i++)
417 nsteps += testspec->sessions[i]->nsteps;
418
419 /* Create PermutationStep workspace array */
420 steps = (PermutationStep *) pg_malloc0(sizeof(PermutationStep) * nsteps);
421 stepptrs = (PermutationStep **) pg_malloc(sizeof(PermutationStep *) * nsteps);
422 for (i = 0; i < nsteps; i++)
423 stepptrs[i] = steps + i;
424
425 /*
426 * To generate the permutations, we conceptually put the steps of each
427 * session on a pile. To generate a permutation, we pick steps from the
428 * piles until all piles are empty. By picking steps from piles in
429 * different order, we get different permutations.
430 *
431 * A pile is actually just an integer which tells how many steps we've
432 * already picked from this pile.
433 */
434 piles = pg_malloc(sizeof(int) * testspec->nsessions);
435 for (i = 0; i < testspec->nsessions; i++)
436 piles[i] = 0;
437
438 run_all_permutations_recurse(testspec, piles, 0, stepptrs);
439
440 free(steps);
441 free(stepptrs);
442 free(piles);
443}
static void run_all_permutations_recurse(TestSpec *testspec, int *piles, int nsteps, PermutationStep **steps)

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

Referenced by run_testspec().

◆ run_all_permutations_recurse()

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

Definition at line 446 of file isolationtester.c.

448{
449 int i;
450 bool found = false;
451
452 for (i = 0; i < testspec->nsessions; i++)
453 {
454 /* If there's any more steps in this pile, pick it and recurse */
455 if (piles[i] < testspec->sessions[i]->nsteps)
456 {
457 Step *newstep = testspec->sessions[i]->steps[piles[i]];
458
459 /*
460 * These automatically-generated PermutationSteps never have
461 * blocker conditions. So we need only fill these fields, relying
462 * on run_all_permutations() to have zeroed the rest:
463 */
464 steps[nsteps]->name = newstep->name;
465 steps[nsteps]->step = newstep;
466
467 piles[i]++;
468
469 run_all_permutations_recurse(testspec, piles, nsteps + 1, steps);
470
471 piles[i]--;
472
473 found = true;
474 }
475 }
476
477 /* If all the piles were empty, this permutation is completed. Run it */
478 if (!found)
479 run_permutation(testspec, nsteps, steps);
480}
static void run_permutation(TestSpec *testspec, int nsteps, PermutationStep **steps)
char * name

References i, Step::name, PermutationStep::name, TestSpec::nsessions, Session::nsteps, run_all_permutations_recurse(), run_permutation(), TestSpec::sessions, PermutationStep::step, and Session::steps.

Referenced by run_all_permutations(), and run_all_permutations_recurse().

◆ run_named_permutations()

static void run_named_permutations ( TestSpec testspec)
static

Definition at line 486 of file isolationtester.c.

487{
488 int i;
489
490 for (i = 0; i < testspec->npermutations; i++)
491 {
492 Permutation *p = testspec->permutations[i];
493
494 run_permutation(testspec, p->nsteps, p->steps);
495 }
496}

References i, TestSpec::npermutations, Permutation::nsteps, TestSpec::permutations, run_permutation(), and Permutation::steps.

Referenced by run_testspec().

◆ run_permutation()

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

Definition at line 520 of file isolationtester.c.

521{
522 PGresult *res;
523 int i;
524 int nwaiting = 0;
526
527 waiting = pg_malloc(sizeof(PermutationStep *) * testspec->nsessions);
528
529 printf("\nstarting permutation:");
530 for (i = 0; i < nsteps; i++)
531 printf(" %s", steps[i]->name);
532 printf("\n");
533
534 /* Perform setup */
535 for (i = 0; i < testspec->nsetupsqls; i++)
536 {
537 res = PQexec(conns[0].conn, testspec->setupsqls[i]);
539 {
541 }
543 {
544 fprintf(stderr, "setup failed: %s", PQerrorMessage(conns[0].conn));
545 exit(1);
546 }
547 PQclear(res);
548 }
549
550 /* Perform per-session setup */
551 for (i = 0; i < testspec->nsessions; i++)
552 {
553 if (testspec->sessions[i]->setupsql)
554 {
555 res = PQexec(conns[i + 1].conn, testspec->sessions[i]->setupsql);
557 {
559 }
561 {
562 fprintf(stderr, "setup of session %s failed: %s",
563 conns[i + 1].sessionname,
564 PQerrorMessage(conns[i + 1].conn));
565 exit(1);
566 }
567 PQclear(res);
568 }
569 }
570
571 /* Perform steps */
572 for (i = 0; i < nsteps; i++)
573 {
574 PermutationStep *pstep = steps[i];
575 Step *step = pstep->step;
576 IsoConnInfo *iconn = &conns[1 + step->session];
577 PGconn *conn = iconn->conn;
578 bool mustwait;
579 int j;
580
581 /*
582 * Check whether the session that needs to perform the next step is
583 * still blocked on an earlier step. If so, wait for it to finish.
584 */
585 if (iconn->active_step != NULL)
586 {
587 struct timeval start_time;
588
589 gettimeofday(&start_time, NULL);
590
591 while (iconn->active_step != NULL)
592 {
593 PermutationStep *oldstep = iconn->active_step;
594
595 /*
596 * Wait for oldstep. But even though we don't use
597 * STEP_NONBLOCK, it might not complete because of blocker
598 * conditions.
599 */
600 if (!try_complete_step(testspec, oldstep, STEP_RETRY))
601 {
602 /* Done, so remove oldstep from the waiting[] array. */
603 int w;
604
605 for (w = 0; w < nwaiting; w++)
606 {
607 if (oldstep == waiting[w])
608 break;
609 }
610 if (w >= nwaiting)
611 abort(); /* can't happen */
612 if (w + 1 < nwaiting)
613 memmove(&waiting[w], &waiting[w + 1],
614 (nwaiting - (w + 1)) * sizeof(PermutationStep *));
615 nwaiting--;
616 }
617
618 /*
619 * Check for other steps that have finished. We should do
620 * this if oldstep completed, as it might have unblocked
621 * something. On the other hand, if oldstep hasn't completed,
622 * we must poll all the active steps in hopes of unblocking
623 * oldstep. So either way, poll them.
624 */
625 nwaiting = try_complete_steps(testspec, waiting, nwaiting,
627
628 /*
629 * If the target session is still busy, apply a timeout to
630 * keep from hanging indefinitely, which could happen with
631 * incorrect blocker annotations. Use the same 2 *
632 * max_step_wait limit as try_complete_step does for deciding
633 * to die. (We don't bother with trying to cancel anything,
634 * since it's unclear what to cancel in this case.)
635 */
636 if (iconn->active_step != NULL)
637 {
638 struct timeval current_time;
639 int64 td;
640
641 gettimeofday(&current_time, NULL);
642 td = (int64) current_time.tv_sec - (int64) start_time.tv_sec;
643 td *= USECS_PER_SEC;
644 td += (int64) current_time.tv_usec - (int64) start_time.tv_usec;
645 if (td > 2 * max_step_wait)
646 {
647 fprintf(stderr, "step %s timed out after %d seconds\n",
648 iconn->active_step->name,
649 (int) (td / USECS_PER_SEC));
650 fprintf(stderr, "active steps are:");
651 for (j = 1; j < nconns; j++)
652 {
653 IsoConnInfo *oconn = &conns[j];
654
655 if (oconn->active_step != NULL)
656 fprintf(stderr, " %s",
657 oconn->active_step->name);
658 }
659 fprintf(stderr, "\n");
660 exit(1);
661 }
662 }
663 }
664 }
665
666 /* Send the query for this step. */
667 if (!PQsendQuery(conn, step->sql))
668 {
669 fprintf(stdout, "failed to send query for step %s: %s\n",
670 step->name, PQerrorMessage(conn));
671 exit(1);
672 }
673
674 /* Remember we launched a step. */
675 iconn->active_step = pstep;
676
677 /* Remember target number of NOTICEs for any blocker conditions. */
678 for (j = 0; j < pstep->nblockers; j++)
679 {
680 PermutationStepBlocker *blocker = pstep->blockers[j];
681
682 if (blocker->blocktype == PSB_NUM_NOTICES)
683 blocker->target_notices = blocker->num_notices +
684 conns[blocker->step->session + 1].total_notices;
685 }
686
687 /* Try to complete this step without blocking. */
688 mustwait = try_complete_step(testspec, pstep, STEP_NONBLOCK);
689
690 /* Check for completion of any steps that were previously waiting. */
691 nwaiting = try_complete_steps(testspec, waiting, nwaiting,
693
694 /* If this step is waiting, add it to the array of waiters. */
695 if (mustwait)
696 waiting[nwaiting++] = pstep;
697 }
698
699 /* Wait for any remaining queries. */
700 nwaiting = try_complete_steps(testspec, waiting, nwaiting, STEP_RETRY);
701 if (nwaiting != 0)
702 {
703 fprintf(stderr, "failed to complete permutation due to mutually-blocking steps\n");
704 exit(1);
705 }
706
707 /* Perform per-session teardown */
708 for (i = 0; i < testspec->nsessions; i++)
709 {
710 if (testspec->sessions[i]->teardownsql)
711 {
712 res = PQexec(conns[i + 1].conn, testspec->sessions[i]->teardownsql);
714 {
716 }
718 {
719 fprintf(stderr, "teardown of session %s failed: %s",
720 conns[i + 1].sessionname,
721 PQerrorMessage(conns[i + 1].conn));
722 /* don't exit on teardown failure */
723 }
724 PQclear(res);
725 }
726 }
727
728 /* Perform teardown */
729 if (testspec->teardownsql)
730 {
731 res = PQexec(conns[0].conn, testspec->teardownsql);
733 {
735 }
737 {
738 fprintf(stderr, "teardown failed: %s",
740 /* don't exit on teardown failure */
741 }
742 PQclear(res);
743 }
744
745 free(waiting);
746}
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1416
PGresult * PQexec(PGconn *conn, const char *query)
Definition: fe-exec.c:2262
static void printResultSet(PGresult *res)
#define STEP_NONBLOCK
static int try_complete_steps(TestSpec *testspec, PermutationStep **waiting, int nwaiting, int flags)
static bool try_complete_step(TestSpec *testspec, PermutationStep *pstep, int flags)
#define STEP_RETRY
@ PSB_NUM_NOTICES
static volatile sig_atomic_t waiting
Definition: latch.c:162
static time_t start_time
Definition: pg_ctl.c:95
PermutationStep * active_step
char * teardownsql
char * setupsql
char * sql
char ** setupsqls
char * teardownsql
int gettimeofday(struct timeval *tp, void *tzp)

References IsoConnInfo::active_step, PermutationStep::blockers, PermutationStepBlocker::blocktype, conn, IsoConnInfo::conn, conns, exit(), fprintf, free, gettimeofday(), i, j, max_step_wait, name, Step::name, PermutationStep::name, PermutationStep::nblockers, nconns, TestSpec::nsessions, TestSpec::nsetupsqls, PermutationStepBlocker::num_notices, pg_malloc(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQerrorMessage(), PQexec(), PQresultStatus(), PQsendQuery(), printf, printResultSet(), PSB_NUM_NOTICES, res, Step::session, TestSpec::sessions, Session::setupsql, TestSpec::setupsqls, Step::sql, start_time, generate_unaccent_rules::stdout, PermutationStepBlocker::step, PermutationStep::step, STEP_NONBLOCK, STEP_RETRY, PermutationStepBlocker::target_notices, Session::teardownsql, TestSpec::teardownsql, IsoConnInfo::total_notices, try_complete_step(), try_complete_steps(), USECS_PER_SEC, and waiting.

Referenced by run_all_permutations_recurse(), and run_named_permutations().

◆ run_testspec()

static void run_testspec ( TestSpec testspec)
static

Definition at line 394 of file isolationtester.c.

395{
396 if (testspec->permutations)
397 run_named_permutations(testspec);
398 else
399 run_all_permutations(testspec);
400}
static void run_named_permutations(TestSpec *testspec)
static void run_all_permutations(TestSpec *testspec)

References TestSpec::permutations, run_all_permutations(), and run_named_permutations().

Referenced by main().

◆ step_bsearch_cmp()

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

Definition at line 508 of file isolationtester.c.

509{
510 char *stepname = (char *) a;
511 Step *step = *((Step **) b);
512
513 return strcmp(stepname, step->name);
514}
int b
Definition: isn.c:69
int a
Definition: isn.c:68

References a, b, and Step::name.

Referenced by check_testspec().

◆ step_has_blocker()

static bool step_has_blocker ( PermutationStep pstep)
static

Definition at line 1080 of file isolationtester.c.

1081{
1082 int i;
1083
1084 for (i = 0; i < pstep->nblockers; i++)
1085 {
1086 PermutationStepBlocker *blocker = pstep->blockers[i];
1087 IsoConnInfo *iconn;
1088
1089 switch (blocker->blocktype)
1090 {
1091 case PSB_ONCE:
1092 /* Ignore; try_complete_step handles this specially */
1093 break;
1094 case PSB_OTHER_STEP:
1095 /* Block if referenced step is active */
1096 iconn = &conns[1 + blocker->step->session];
1097 if (iconn->active_step &&
1098 iconn->active_step->step == blocker->step)
1099 return true;
1100 break;
1101 case PSB_NUM_NOTICES:
1102 /* Block if not enough notices received yet */
1103 iconn = &conns[1 + blocker->step->session];
1104 if (iconn->total_notices < blocker->target_notices)
1105 return true;
1106 break;
1107 }
1108 }
1109 return false;
1110}
@ PSB_OTHER_STEP

References IsoConnInfo::active_step, PermutationStep::blockers, PermutationStepBlocker::blocktype, conns, i, PermutationStep::nblockers, PSB_NUM_NOTICES, PSB_ONCE, PSB_OTHER_STEP, Step::session, PermutationStepBlocker::step, PermutationStep::step, PermutationStepBlocker::target_notices, and IsoConnInfo::total_notices.

Referenced by try_complete_step().

◆ step_qsort_cmp()

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

Definition at line 499 of file isolationtester.c.

500{
501 Step *stepa = *((Step **) a);
502 Step *stepb = *((Step **) b);
503
504 return strcmp(stepa->name, stepb->name);
505}

References a, b, and Step::name.

Referenced by check_testspec().

◆ try_complete_step()

static bool try_complete_step ( TestSpec testspec,
PermutationStep pstep,
int  flags 
)
static

Definition at line 818 of file isolationtester.c.

819{
820 Step *step = pstep->step;
821 IsoConnInfo *iconn = &conns[1 + step->session];
822 PGconn *conn = iconn->conn;
823 fd_set read_set;
824 struct timeval start_time;
825 struct timeval timeout;
826 int sock = PQsocket(conn);
827 int ret;
828 PGresult *res;
829 PGnotify *notify;
830 bool canceled = false;
831
832 /*
833 * If the step is annotated with (*), then on the first call, force it to
834 * wait. This is useful for ensuring consistent output when the step
835 * might or might not complete so fast that we don't observe it waiting.
836 */
837 if (!(flags & STEP_RETRY))
838 {
839 int i;
840
841 for (i = 0; i < pstep->nblockers; i++)
842 {
843 PermutationStepBlocker *blocker = pstep->blockers[i];
844
845 if (blocker->blocktype == PSB_ONCE)
846 {
847 printf("step %s: %s <waiting ...>\n",
848 step->name, step->sql);
849 return true;
850 }
851 }
852 }
853
854 if (sock < 0)
855 {
856 fprintf(stderr, "invalid socket: %s", PQerrorMessage(conn));
857 exit(1);
858 }
859
860 gettimeofday(&start_time, NULL);
861 FD_ZERO(&read_set);
862
863 while (PQisBusy(conn))
864 {
865 FD_SET(sock, &read_set);
866 timeout.tv_sec = 0;
867 timeout.tv_usec = 10000; /* Check for lock waits every 10ms. */
868
869 ret = select(sock + 1, &read_set, NULL, NULL, &timeout);
870 if (ret < 0) /* error in select() */
871 {
872 if (errno == EINTR)
873 continue;
874 fprintf(stderr, "select failed: %m\n");
875 exit(1);
876 }
877 else if (ret == 0) /* select() timeout: check for lock wait */
878 {
879 struct timeval current_time;
880 int64 td;
881
882 /* If it's OK for the step to block, check whether it has. */
883 if (flags & STEP_NONBLOCK)
884 {
885 bool waiting;
886
888 &conns[step->session + 1].backend_pid_str,
889 NULL, NULL, 0);
891 PQntuples(res) != 1)
892 {
893 fprintf(stderr, "lock wait query failed: %s",
895 exit(1);
896 }
897 waiting = ((PQgetvalue(res, 0, 0))[0] == 't');
898 PQclear(res);
899
900 if (waiting) /* waiting to acquire a lock */
901 {
902 /*
903 * Since it takes time to perform the lock-check query,
904 * some data --- notably, NOTICE messages --- might have
905 * arrived since we looked. We must call PQconsumeInput
906 * and then PQisBusy to collect and process any such
907 * messages. In the (unlikely) case that PQisBusy then
908 * returns false, we might as well go examine the
909 * available result.
910 */
911 if (!PQconsumeInput(conn))
912 {
913 fprintf(stderr, "PQconsumeInput failed: %s\n",
915 exit(1);
916 }
917 if (!PQisBusy(conn))
918 break;
919
920 /*
921 * conn is still busy, so conclude that the step really is
922 * waiting.
923 */
924 if (!(flags & STEP_RETRY))
925 printf("step %s: %s <waiting ...>\n",
926 step->name, step->sql);
927 return true;
928 }
929 /* else, not waiting */
930 }
931
932 /* Figure out how long we've been waiting for this step. */
933 gettimeofday(&current_time, NULL);
934 td = (int64) current_time.tv_sec - (int64) start_time.tv_sec;
935 td *= USECS_PER_SEC;
936 td += (int64) current_time.tv_usec - (int64) start_time.tv_usec;
937
938 /*
939 * After max_step_wait microseconds, try to cancel the query.
940 *
941 * If the user tries to test an invalid permutation, we don't want
942 * to hang forever, especially when this is running in the
943 * buildfarm. This will presumably lead to this permutation
944 * failing, but remaining permutations and tests should still be
945 * OK.
946 */
947 if (td > max_step_wait && !canceled)
948 {
949 PGcancelConn *cancel_conn = PQcancelCreate(conn);
950
951 if (PQcancelBlocking(cancel_conn))
952 {
953 /*
954 * print to stdout not stderr, as this should appear in
955 * the test case's results
956 */
957 printf("isolationtester: canceling step %s after %d seconds\n",
958 step->name, (int) (td / USECS_PER_SEC));
959 canceled = true;
960 }
961 else
962 fprintf(stderr, "PQcancel failed: %s\n", PQcancelErrorMessage(cancel_conn));
963 PQcancelFinish(cancel_conn);
964 }
965
966 /*
967 * After twice max_step_wait, just give up and die.
968 *
969 * Since cleanup steps won't be run in this case, this may cause
970 * later tests to fail. That stinks, but it's better than waiting
971 * forever for the server to respond to the cancel.
972 */
973 if (td > 2 * max_step_wait)
974 {
975 fprintf(stderr, "step %s timed out after %d seconds\n",
976 step->name, (int) (td / USECS_PER_SEC));
977 exit(1);
978 }
979 }
980 else if (!PQconsumeInput(conn)) /* select(): data available */
981 {
982 fprintf(stderr, "PQconsumeInput failed: %s\n",
984 exit(1);
985 }
986 }
987
988 /*
989 * The step is done, but we won't report it as complete so long as there
990 * are blockers.
991 */
992 if (step_has_blocker(pstep))
993 {
994 if (!(flags & STEP_RETRY))
995 printf("step %s: %s <waiting ...>\n",
996 step->name, step->sql);
997 return true;
998 }
999
1000 /* Otherwise, go ahead and complete it. */
1001 if (flags & STEP_RETRY)
1002 printf("step %s: <... completed>\n", step->name);
1003 else
1004 printf("step %s: %s\n", step->name, step->sql);
1005
1006 while ((res = PQgetResult(conn)))
1007 {
1008 switch (PQresultStatus(res))
1009 {
1010 case PGRES_COMMAND_OK:
1011 case PGRES_EMPTY_QUERY:
1012 break;
1013 case PGRES_TUPLES_OK:
1015 break;
1016 case PGRES_FATAL_ERROR:
1017
1018 /*
1019 * Detail may contain XID values, so we want to just show
1020 * primary. Beware however that libpq-generated error results
1021 * may not contain subfields, only an old-style message.
1022 */
1023 {
1024 const char *sev = PQresultErrorField(res,
1026 const char *msg = PQresultErrorField(res,
1028
1029 if (sev && msg)
1030 printf("%s: %s\n", sev, msg);
1031 else
1033 }
1034 break;
1035 default:
1036 printf("unexpected result status: %s\n",
1038 }
1039 PQclear(res);
1040 }
1041
1042 /* Report any available NOTIFY messages, too */
1044 while ((notify = PQnotifies(conn)) != NULL)
1045 {
1046 /* Try to identify which session it came from */
1047 const char *sendername = NULL;
1048 char pidstring[32];
1049 int i;
1050
1051 for (i = 0; i < testspec->nsessions; i++)
1052 {
1053 if (notify->be_pid == conns[i + 1].backend_pid)
1054 {
1055 sendername = conns[i + 1].sessionname;
1056 break;
1057 }
1058 }
1059 if (sendername == NULL)
1060 {
1061 /* Doesn't seem to be any test session, so show the hard way */
1062 snprintf(pidstring, sizeof(pidstring), "PID %d", notify->be_pid);
1063 sendername = pidstring;
1064 }
1065 printf("%s: NOTIFY \"%s\" with payload \"%s\" from %s\n",
1066 testspec->sessions[step->session]->name,
1067 notify->relname, notify->extra, sendername);
1068 PQfreemem(notify);
1070 }
1071
1072 /* Connection is now idle. */
1073 iconn->active_step = NULL;
1074
1075 return false;
1076}
PGcancelConn * PQcancelCreate(PGconn *conn)
Definition: fe-cancel.c:65
int PQcancelBlocking(PGcancelConn *cancelConn)
Definition: fe-cancel.c:171
void PQcancelFinish(PGcancelConn *cancelConn)
Definition: fe-cancel.c:334
char * PQcancelErrorMessage(const PGcancelConn *cancelConn)
Definition: fe-cancel.c:306
int PQsocket(const PGconn *conn)
Definition: fe-connect.c:7294
void PQfreemem(void *ptr)
Definition: fe-exec.c:4032
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:2323
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3876
PGresult * PQgetResult(PGconn *conn)
Definition: fe-exec.c:2062
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3481
char * PQresultErrorMessage(const PGresult *res)
Definition: fe-exec.c:3427
int PQconsumeInput(PGconn *conn)
Definition: fe-exec.c:1984
char * PQresultErrorField(const PGresult *res, int fieldcode)
Definition: fe-exec.c:3466
char * PQresStatus(ExecStatusType status)
Definition: fe-exec.c:3419
int PQisBusy(PGconn *conn)
Definition: fe-exec.c:2031
PGnotify * PQnotifies(PGconn *conn)
Definition: fe-exec.c:2667
static bool step_has_blocker(PermutationStep *pstep)
@ PGRES_FATAL_ERROR
Definition: libpq-fe.h:131
@ PGRES_EMPTY_QUERY
Definition: libpq-fe.h:119
#define snprintf
Definition: port.h:238
#define PG_DIAG_MESSAGE_PRIMARY
Definition: postgres_ext.h:58
#define PG_DIAG_SEVERITY
Definition: postgres_ext.h:55
int be_pid
Definition: libpq-fe.h:219
char * relname
Definition: libpq-fe.h:218
char * extra
Definition: libpq-fe.h:220
#define EINTR
Definition: win32_port.h:364
#define select(n, r, w, e, timeout)
Definition: win32_port.h:503

References IsoConnInfo::active_step, IsoConnInfo::backend_pid, IsoConnInfo::backend_pid_str, pgNotify::be_pid, PermutationStep::blockers, PermutationStepBlocker::blocktype, conn, IsoConnInfo::conn, conns, EINTR, exit(), pgNotify::extra, fprintf, gettimeofday(), i, max_step_wait, Session::name, Step::name, PermutationStep::nblockers, TestSpec::nsessions, PG_DIAG_MESSAGE_PRIMARY, PG_DIAG_SEVERITY, PGRES_COMMAND_OK, PGRES_EMPTY_QUERY, PGRES_FATAL_ERROR, PGRES_TUPLES_OK, PQcancelBlocking(), PQcancelCreate(), PQcancelErrorMessage(), PQcancelFinish(), PQclear(), PQconsumeInput(), PQerrorMessage(), PQexecPrepared(), PQfreemem(), PQgetResult(), PQgetvalue(), PQisBusy(), PQnotifies(), PQntuples(), PQresStatus(), PQresultErrorField(), PQresultErrorMessage(), PQresultStatus(), PQsocket(), PREP_WAITING, printf, printResultSet(), PSB_ONCE, pgNotify::relname, res, select, Step::session, IsoConnInfo::sessionname, TestSpec::sessions, snprintf, Step::sql, start_time, PermutationStep::step, step_has_blocker(), STEP_NONBLOCK, STEP_RETRY, USECS_PER_SEC, and waiting.

Referenced by run_permutation(), and try_complete_steps().

◆ try_complete_steps()

static int try_complete_steps ( TestSpec testspec,
PermutationStep **  waiting,
int  nwaiting,
int  flags 
)
static

Definition at line 755 of file isolationtester.c.

757{
758 int old_nwaiting;
759 bool have_blocker;
760
761 do
762 {
763 int w = 0;
764
765 /* Reset latch; we only care about notices received within loop. */
766 any_new_notice = false;
767
768 /* Likewise, these variables reset for each retry. */
769 old_nwaiting = nwaiting;
770 have_blocker = false;
771
772 /* Scan the array, try to complete steps. */
773 while (w < nwaiting)
774 {
775 if (try_complete_step(testspec, waiting[w], flags))
776 {
777 /* Still blocked, leave it alone. */
778 if (waiting[w]->nblockers > 0)
779 have_blocker = true;
780 w++;
781 }
782 else
783 {
784 /* Done, remove it from array. */
785 if (w + 1 < nwaiting)
786 memmove(&waiting[w], &waiting[w + 1],
787 (nwaiting - (w + 1)) * sizeof(PermutationStep *));
788 nwaiting--;
789 }
790 }
791
792 /*
793 * If any of the still-waiting steps have blocker conditions attached,
794 * it's possible that one of the steps we examined afterwards has
795 * released them (either by completing, or by sending a NOTICE). If
796 * any step completions or NOTICEs happened, repeat the loop until
797 * none occurs. Without this provision, completion timing could vary
798 * depending on the order in which the steps appear in the array.
799 */
800 } while (have_blocker && (nwaiting < old_nwaiting || any_new_notice));
801 return nwaiting;
802}

References any_new_notice, try_complete_step(), and waiting.

Referenced by run_permutation().

Variable Documentation

◆ any_new_notice

bool any_new_notice = false
static

Definition at line 44 of file isolationtester.c.

Referenced by isotesterNoticeProcessor(), and try_complete_steps().

◆ conns

IsoConnInfo* conns = NULL
static

◆ max_step_wait

int64 max_step_wait = 360 * USECS_PER_SEC
static

Definition at line 47 of file isolationtester.c.

Referenced by main(), run_permutation(), and try_complete_step().

◆ nconns

int nconns = 0
static

Definition at line 41 of file isolationtester.c.

Referenced by disconnect_atexit(), main(), and run_permutation().