/* *conns[0]istheglobalsetup,teardown,andwatchdogconnection.Additional *connectionsrepresentspec-definedsessions.
*/ typedefstruct IsoConnInfo
{ /* The libpq connection object for this connection. */
PGconn *conn; /* The backend PID, in numeric and string formats. */ int backend_pid; constchar *backend_pid_str; /* Name of the associated session. */ constchar *sessionname; /* Active step on this connection, or NULL if idle. */
PermutationStep *active_step; /* Number of NOTICE messages received from connection. */ int total_notices;
} IsoConnInfo;
/* Flag indicating some new NOTICE has arrived */ staticbool any_new_notice = false;
/* Maximum time to wait before giving up on a step (in usec) */ static int64 max_step_wait = 360 * USECS_PER_SEC;
staticvoid check_testspec(TestSpec *testspec); staticvoid run_testspec(TestSpec *testspec); staticvoid run_all_permutations(TestSpec *testspec); staticvoid run_all_permutations_recurse(TestSpec *testspec, int *piles, int nsteps, PermutationStep **steps); staticvoid run_named_permutations(TestSpec *testspec); staticvoid run_permutation(TestSpec *testspec, int nsteps,
PermutationStep **steps);
/* Flag bits for try_complete_step(s) */ #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 */
staticint try_complete_steps(TestSpec *testspec, PermutationStep **waiting, int nwaiting, int flags); staticbool try_complete_step(TestSpec *testspec, PermutationStep *pstep, int flags);
/* *Validity-checkthetestspecandfillincross-linksbetweennodes.
*/ staticvoid
check_testspec(TestSpec *testspec)
{ int nallsteps;
Step **allsteps; int i,
j,
k;
/* Create a sorted lookup table of all steps. */
nallsteps = 0; for (i = 0; i < testspec->nsessions; i++)
nallsteps += testspec->sessions[i]->nsteps;
allsteps = pg_malloc(nallsteps * sizeof(Step *));
k = 0; for (i = 0; i < testspec->nsessions; i++)
{ for (j = 0; j < testspec->sessions[i]->nsteps; j++)
allsteps[k++] = testspec->sessions[i]->steps[j];
}
/* Verify that all step names are unique. */ for (i = 1; i < nallsteps; i++)
{ if (strcmp(allsteps[i - 1]->name,
allsteps[i]->name) == 0)
{
fprintf(stderr, "duplicate step name: %s\n",
allsteps[i]->name); exit(1);
}
}
/* Set the session index fields in steps. */ for (i = 0; i < testspec->nsessions; i++)
{
Session *session = testspec->sessions[i];
staticvoid
run_all_permutations_recurse(TestSpec *testspec, int *piles, int nsteps, PermutationStep **steps)
{ int i; bool found = false;
for (i = 0; i < testspec->nsessions; i++)
{ /* If there's any more steps in this pile, pick it and recurse */ if (piles[i] < testspec->sessions[i]->nsteps)
{
Step *newstep = testspec->sessions[i]->steps[piles[i]];
while (iconn->active_step != NULL)
{
PermutationStep *oldstep = iconn->active_step;
/* *Waitforoldstep.Buteventhoughwedon'tuse *STEP_NONBLOCK,itmightnotcompletebecauseofblocker *conditions.
*/ if (!try_complete_step(testspec, oldstep, STEP_RETRY))
{ /* Done, so remove oldstep from the waiting[] array. */ int w;
for (w = 0; w < nwaiting; w++)
{ if (oldstep == waiting[w]) break;
} if (w >= nwaiting)
abort(); /* can't happen */ if (w + 1 < nwaiting)
memmove(&waiting[w], &waiting[w + 1],
(nwaiting - (w + 1)) * sizeof(PermutationStep *));
nwaiting--;
}
/* Send the query for this step. */ if (!PQsendQuery(conn, step->sql))
{
fprintf(stdout, "failed to send query for step %s: %s\n",
step->name, PQerrorMessage(conn)); exit(1);
}
/* Remember we launched a step. */
iconn->active_step = pstep;
/* Remember target number of NOTICEs for any blocker conditions. */ for (j = 0; j < pstep->nblockers; j++)
{
PermutationStepBlocker *blocker = pstep->blockers[j];
/* Try to complete this step without blocking. */
mustwait = try_complete_step(testspec, pstep, STEP_NONBLOCK);
/* Check for completion of any steps that were previously waiting. */
nwaiting = try_complete_steps(testspec, waiting, nwaiting,
STEP_NONBLOCK | STEP_RETRY);
/* If this step is waiting, add it to the array of waiters. */ if (mustwait)
waiting[nwaiting++] = pstep;
}
/* Wait for any remaining queries. */
nwaiting = try_complete_steps(testspec, waiting, nwaiting, STEP_RETRY); if (nwaiting != 0)
{
fprintf(stderr, "failed to complete permutation due to mutually-blocking steps\n"); exit(1);
}
/* Perform per-session teardown */ for (i = 0; i < testspec->nsessions; i++)
{ if (testspec->sessions[i]->teardownsql)
{
res = PQexec(conns[i + 1].conn, testspec->sessions[i]->teardownsql); if (PQresultStatus(res) == PGRES_TUPLES_OK)
{
printResultSet(res);
} elseif (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "teardown of session %s failed: %s",
conns[i + 1].sessionname,
PQerrorMessage(conns[i + 1].conn)); /* don't exit on teardown failure */
}
PQclear(res);
}
}
/* Perform teardown */ if (testspec->teardownsql)
{
res = PQexec(conns[0].conn, testspec->teardownsql); if (PQresultStatus(res) == PGRES_TUPLES_OK)
{
printResultSet(res);
} elseif (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "teardown failed: %s",
PQerrorMessage(conns[0].conn)); /* don't exit on teardown failure */
}
PQclear(res);
}
free(waiting);
}
/* *Checkforcompletionofanywaitingstep(s). *Removecompletedonesfromthewaiting[]array, *andreturnthenewvalueofnwaiting. *Seetry_complete_stepforthemeaningoftheflags.
*/ staticint
try_complete_steps(TestSpec *testspec, PermutationStep **waiting, int nwaiting, int flags)
{ int old_nwaiting; bool have_blocker;
do
{ int w = 0;
/* Reset latch; we only care about notices received within loop. */
any_new_notice = false;
/* Likewise, these variables reset for each retry. */
old_nwaiting = nwaiting;
have_blocker = false;
/* Scan the array, try to complete steps. */ while (w < nwaiting)
{ if (try_complete_step(testspec, waiting[w], flags))
{ /* Still blocked, leave it alone. */ if (waiting[w]->nblockers > 0)
have_blocker = true;
w++;
} else
{ /* Done, remove it from array. */ if (w + 1 < nwaiting)
memmove(&waiting[w], &waiting[w + 1],
(nwaiting - (w + 1)) * sizeof(PermutationStep *));
nwaiting--;
}
}
/* *Aftertwicemax_step_wait,justgiveupanddie. * *Sincecleanupstepswon'tberuninthiscase,thismaycause *laterteststofail.Thatstinks,butit'sbetterthanwaiting *foreverfortheservertorespondtothecancel.
*/ if (td > 2 * max_step_wait)
{
fprintf(stderr, "step %s timed out after %d seconds\n",
step->name, (int) (td / USECS_PER_SEC)); exit(1);
}
} elseif (!PQconsumeInput(conn)) /* select(): data available */
{
fprintf(stderr, "PQconsumeInput failed: %s\n",
PQerrorMessage(conn)); exit(1);
}
}
/* *Thestepisdone,butwewon'treportitascompletesolongasthere *areblockers.
*/ if (step_has_blocker(pstep))
{ if (!(flags & STEP_RETRY))
printf("step %s: %s <waiting ...>\n",
step->name, step->sql); returntrue;
}
/* Otherwise, go ahead and complete it. */ if (flags & STEP_RETRY)
printf("step %s: <... completed>\n", step->name); else
printf("step %s: %s\n", step->name, step->sql);
while ((res = PQgetResult(conn)))
{ switch (PQresultStatus(res))
{ case PGRES_COMMAND_OK: case PGRES_EMPTY_QUERY: break; case PGRES_TUPLES_OK:
printResultSet(res); break; case PGRES_FATAL_ERROR:
/* Report any available NOTIFY messages, too */
PQconsumeInput(conn); while ((notify = PQnotifies(conn)) != NULL)
{ /* Try to identify which session it came from */ constchar *sendername = NULL; char pidstring[32]; int i;
for (i = 0; i < testspec->nsessions; i++)
{ if (notify->be_pid == conns[i + 1].backend_pid)
{
sendername = conns[i + 1].sessionname; break;
}
} if (sendername == NULL)
{ /* Doesn't seem to be any test session, so show the hard way */
snprintf(pidstring, sizeof(pidstring), "PID %d", notify->be_pid);
sendername = pidstring;
}
printf("%s: NOTIFY \"%s\" with payload \"%s\" from %s\n",
testspec->sessions[step->session]->name,
notify->relname, notify->extra, sendername);
PQfreemem(notify);
PQconsumeInput(conn);
}
/* Connection is now idle. */
iconn->active_step = NULL;
returnfalse;
}
/* Detect whether a step has any unsatisfied blocker conditions */ staticbool
step_has_blocker(PermutationStep *pstep)
{ int i;
for (i = 0; i < pstep->nblockers; i++)
{
PermutationStepBlocker *blocker = pstep->blockers[i];
IsoConnInfo *iconn;
switch (blocker->blocktype)
{ case PSB_ONCE: /* Ignore; try_complete_step handles this specially */ break; case PSB_OTHER_STEP: /* Block if referenced step is active */
iconn = &conns[1 + blocker->step->session]; if (iconn->active_step &&
iconn->active_step->step == blocker->step) returntrue; break; case PSB_NUM_NOTICES: /* Block if not enough notices received yet */
iconn = &conns[1 + blocker->step->session]; if (iconn->total_notices < blocker->target_notices) returntrue; break;
}
} returnfalse;
}
/* notice processor for regular user sessions */ staticvoid
isotesterNoticeProcessor(void *arg, constchar *message)
{
IsoConnInfo *myconn = (IsoConnInfo *) arg;
/* Prefix the backend's message with the session name. */
printf("%s: %s", myconn->sessionname, message); /* Record notices, since we may need this to decide to unblock a step. */
myconn->total_notices++;
any_new_notice = true;
}
/* notice processor, hides the message */ staticvoid
blackholeNoticeProcessor(void *arg, constchar *message)
{ /* do nothing */
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.38 Sekunden
(vorverarbeitet am 2026-08-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.