LCOV - code coverage report
Current view: top level - src/test/isolation - isolationtester.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 516 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 16 0
Legend: Lines:     hit not hit

            Line data    Source code
       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/select.h>
      11              : #include <sys/time.h>
      12              : 
      13              : #include "datatype/timestamp.h"
      14              : #include "isolationtester.h"
      15              : #include "libpq-fe.h"
      16              : #include "pg_getopt.h"
      17              : #include "pqexpbuffer.h"
      18              : 
      19              : #define PREP_WAITING "isolationtester_waiting"
      20              : 
      21              : /*
      22              :  * conns[0] is the global setup, teardown, and watchdog connection.  Additional
      23              :  * connections represent spec-defined sessions.
      24              :  */
      25              : typedef struct IsoConnInfo
      26              : {
      27              :         /* The libpq connection object for this connection. */
      28              :         PGconn     *conn;
      29              :         /* The backend PID, in numeric and string formats. */
      30              :         int                     backend_pid;
      31              :         const char *backend_pid_str;
      32              :         /* Name of the associated session. */
      33              :         const char *sessionname;
      34              :         /* Active step on this connection, or NULL if idle. */
      35              :         PermutationStep *active_step;
      36              :         /* Number of NOTICE messages received from connection. */
      37              :         int                     total_notices;
      38              : } IsoConnInfo;
      39              : 
      40              : static IsoConnInfo *conns = NULL;
      41              : static int      nconns = 0;
      42              : 
      43              : /* Flag indicating some new NOTICE has arrived */
      44              : static bool any_new_notice = false;
      45              : 
      46              : /* Maximum time to wait before giving up on a step (in usec) */
      47              : static int64 max_step_wait = 360 * USECS_PER_SEC;
      48              : 
      49              : 
      50              : static void check_testspec(TestSpec *testspec);
      51              : static void run_testspec(TestSpec *testspec);
      52              : static void run_all_permutations(TestSpec *testspec);
      53              : static void run_all_permutations_recurse(TestSpec *testspec, int *piles,
      54              :                                                                                  int nsteps, PermutationStep **steps);
      55              : static void run_named_permutations(TestSpec *testspec);
      56              : static void run_permutation(TestSpec *testspec, int nsteps,
      57              :                                                         PermutationStep **steps);
      58              : 
      59              : /* Flag bits for try_complete_step(s) */
      60              : #define STEP_NONBLOCK   0x1             /* return as soon as cmd waits for a lock */
      61              : #define STEP_RETRY              0x2             /* this is a retry of a previously-waiting cmd */
      62              : 
      63              : static int      try_complete_steps(TestSpec *testspec, PermutationStep **waiting,
      64              :                                                            int nwaiting, int flags);
      65              : static bool try_complete_step(TestSpec *testspec, PermutationStep *pstep,
      66              :                                                           int flags);
      67              : 
      68              : static int      step_qsort_cmp(const void *a, const void *b);
      69              : static int      step_bsearch_cmp(const void *a, const void *b);
      70              : 
      71              : static bool step_has_blocker(PermutationStep *pstep);
      72              : static void printResultSet(PGresult *res);
      73              : static void isotesterNoticeProcessor(void *arg, const char *message);
      74              : static void blackholeNoticeProcessor(void *arg, const char *message);
      75              : 
      76              : static void
      77            0 : disconnect_atexit(void)
      78              : {
      79            0 :         int                     i;
      80              : 
      81            0 :         for (i = 0; i < nconns; i++)
      82            0 :                 if (conns[i].conn)
      83            0 :                         PQfinish(conns[i].conn);
      84            0 : }
      85              : 
      86              : int
      87            0 : main(int argc, char **argv)
      88              : {
      89            0 :         const char *conninfo;
      90            0 :         const char *env_wait;
      91            0 :         TestSpec   *testspec;
      92            0 :         PGresult   *res;
      93            0 :         PQExpBufferData wait_query;
      94            0 :         int                     opt;
      95            0 :         int                     i;
      96              : 
      97            0 :         while ((opt = getopt(argc, argv, "V")) != -1)
      98              :         {
      99            0 :                 switch (opt)
     100              :                 {
     101              :                         case 'V':
     102            0 :                                 puts("isolationtester (PostgreSQL) " PG_VERSION);
     103            0 :                                 exit(0);
     104              :                         default:
     105            0 :                                 fprintf(stderr, "Usage: isolationtester [CONNINFO]\n");
     106            0 :                                 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            0 :         setbuf(stdout, NULL);
     115            0 :         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            0 :         if (argc > optind)
     124            0 :                 conninfo = argv[optind];
     125              :         else
     126            0 :                 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            0 :         env_wait = getenv("PG_TEST_TIMEOUT_DEFAULT");
     133            0 :         if (env_wait != NULL)
     134            0 :                 max_step_wait = 2 * ((int64) atoi(env_wait)) * USECS_PER_SEC;
     135              : 
     136              :         /* Read the test spec from stdin */
     137            0 :         spec_yyparse();
     138            0 :         testspec = &parseresult;
     139              : 
     140              :         /* Perform post-parse checking, and fill in linking fields */
     141            0 :         check_testspec(testspec);
     142              : 
     143            0 :         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            0 :         nconns = 1 + testspec->nsessions;
     150            0 :         conns = (IsoConnInfo *) pg_malloc0(nconns * sizeof(IsoConnInfo));
     151            0 :         atexit(disconnect_atexit);
     152              : 
     153            0 :         for (i = 0; i < nconns; i++)
     154              :         {
     155            0 :                 const char *sessionname;
     156              : 
     157            0 :                 if (i == 0)
     158            0 :                         sessionname = "control connection";
     159              :                 else
     160            0 :                         sessionname = testspec->sessions[i - 1]->name;
     161              : 
     162            0 :                 conns[i].sessionname = sessionname;
     163              : 
     164            0 :                 conns[i].conn = PQconnectdb(conninfo);
     165            0 :                 if (PQstatus(conns[i].conn) != CONNECTION_OK)
     166              :                 {
     167            0 :                         fprintf(stderr, "Connection %d failed: %s",
     168            0 :                                         i, PQerrorMessage(conns[i].conn));
     169            0 :                         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            0 :                 if (i != 0)
     179            0 :                         PQsetNoticeProcessor(conns[i].conn,
     180              :                                                                  isotesterNoticeProcessor,
     181            0 :                                                                  &conns[i]);
     182              :                 else
     183            0 :                         PQsetNoticeProcessor(conns[i].conn,
     184              :                                                                  blackholeNoticeProcessor,
     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              :                  */
     193            0 :                 res = PQexecParams(conns[i].conn,
     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);
     200            0 :                 if (PQresultStatus(res) != PGRES_TUPLES_OK)
     201              :                 {
     202            0 :                         fprintf(stderr, "setting of application name failed: %s",
     203            0 :                                         PQerrorMessage(conns[i].conn));
     204            0 :                         exit(1);
     205              :                 }
     206              : 
     207              :                 /* Save each connection's backend PID for subsequent use. */
     208            0 :                 conns[i].backend_pid = PQbackendPID(conns[i].conn);
     209            0 :                 conns[i].backend_pid_str = psprintf("%d", conns[i].backend_pid);
     210            0 :         }
     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            0 :         initPQExpBuffer(&wait_query);
     221            0 :         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            0 :         appendPQExpBufferStr(&wait_query, conns[1].backend_pid_str);
     225            0 :         for (i = 2; i < nconns; i++)
     226            0 :                 appendPQExpBuffer(&wait_query, ",%s", conns[i].backend_pid_str);
     227            0 :         appendPQExpBufferStr(&wait_query, "}')");
     228              : 
     229            0 :         res = PQprepare(conns[0].conn, PREP_WAITING, wait_query.data, 0, NULL);
     230            0 :         if (PQresultStatus(res) != PGRES_COMMAND_OK)
     231              :         {
     232            0 :                 fprintf(stderr, "prepare of lock wait query failed: %s",
     233            0 :                                 PQerrorMessage(conns[0].conn));
     234            0 :                 exit(1);
     235              :         }
     236            0 :         PQclear(res);
     237            0 :         termPQExpBuffer(&wait_query);
     238              : 
     239              :         /*
     240              :          * Run the permutations specified in the spec, or all if none were
     241              :          * explicitly specified.
     242              :          */
     243            0 :         run_testspec(testspec);
     244              : 
     245            0 :         return 0;
     246            0 : }
     247              : 
     248              : /*
     249              :  * Validity-check the test spec and fill in cross-links between nodes.
     250              :  */
     251              : static void
     252            0 : check_testspec(TestSpec *testspec)
     253              : {
     254            0 :         int                     nallsteps;
     255            0 :         Step      **allsteps;
     256            0 :         int                     i,
     257              :                                 j,
     258              :                                 k;
     259              : 
     260              :         /* Create a sorted lookup table of all steps. */
     261            0 :         nallsteps = 0;
     262            0 :         for (i = 0; i < testspec->nsessions; i++)
     263            0 :                 nallsteps += testspec->sessions[i]->nsteps;
     264              : 
     265            0 :         allsteps = pg_malloc(nallsteps * sizeof(Step *));
     266              : 
     267            0 :         k = 0;
     268            0 :         for (i = 0; i < testspec->nsessions; i++)
     269              :         {
     270            0 :                 for (j = 0; j < testspec->sessions[i]->nsteps; j++)
     271            0 :                         allsteps[k++] = testspec->sessions[i]->steps[j];
     272            0 :         }
     273              : 
     274            0 :         qsort(allsteps, nallsteps, sizeof(Step *), step_qsort_cmp);
     275              : 
     276              :         /* Verify that all step names are unique. */
     277            0 :         for (i = 1; i < nallsteps; i++)
     278              :         {
     279            0 :                 if (strcmp(allsteps[i - 1]->name,
     280            0 :                                    allsteps[i]->name) == 0)
     281              :                 {
     282            0 :                         fprintf(stderr, "duplicate step name: %s\n",
     283            0 :                                         allsteps[i]->name);
     284            0 :                         exit(1);
     285              :                 }
     286            0 :         }
     287              : 
     288              :         /* Set the session index fields in steps. */
     289            0 :         for (i = 0; i < testspec->nsessions; i++)
     290              :         {
     291            0 :                 Session    *session = testspec->sessions[i];
     292              : 
     293            0 :                 for (j = 0; j < session->nsteps; j++)
     294            0 :                         session->steps[j]->session = i;
     295            0 :         }
     296              : 
     297              :         /*
     298              :          * If we have manually-specified permutations, link PermutationSteps to
     299              :          * Steps, and fill in blocker links.
     300              :          */
     301            0 :         for (i = 0; i < testspec->npermutations; i++)
     302              :         {
     303            0 :                 Permutation *p = testspec->permutations[i];
     304              : 
     305            0 :                 for (j = 0; j < p->nsteps; j++)
     306              :                 {
     307            0 :                         PermutationStep *pstep = p->steps[j];
     308            0 :                         Step      **this = (Step **) bsearch(pstep->name,
     309            0 :                                                                                                  allsteps,
     310            0 :                                                                                                  nallsteps,
     311              :                                                                                                  sizeof(Step *),
     312              :                                                                                                  step_bsearch_cmp);
     313              : 
     314            0 :                         if (this == NULL)
     315              :                         {
     316            0 :                                 fprintf(stderr, "undefined step \"%s\" specified in permutation\n",
     317            0 :                                                 pstep->name);
     318            0 :                                 exit(1);
     319              :                         }
     320            0 :                         pstep->step = *this;
     321              : 
     322              :                         /* Mark the step used, for check below */
     323            0 :                         pstep->step->used = true;
     324            0 :                 }
     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            0 :                 for (j = 0; j < p->nsteps; j++)
     333              :                 {
     334            0 :                         PermutationStep *pstep = p->steps[j];
     335              : 
     336            0 :                         for (k = 0; k < pstep->nblockers; k++)
     337              :                         {
     338            0 :                                 PermutationStepBlocker *blocker = pstep->blockers[k];
     339            0 :                                 int                     n;
     340              : 
     341            0 :                                 if (blocker->blocktype == PSB_ONCE)
     342            0 :                                         continue;       /* nothing to link to */
     343              : 
     344            0 :                                 blocker->step = NULL;
     345            0 :                                 for (n = 0; n < p->nsteps; n++)
     346              :                                 {
     347            0 :                                         PermutationStep *otherp = p->steps[n];
     348              : 
     349            0 :                                         if (strcmp(otherp->name, blocker->stepname) == 0)
     350              :                                         {
     351            0 :                                                 blocker->step = otherp->step;
     352            0 :                                                 break;
     353              :                                         }
     354            0 :                                 }
     355            0 :                                 if (blocker->step == NULL)
     356              :                                 {
     357            0 :                                         fprintf(stderr, "undefined blocking step \"%s\" referenced in permutation step \"%s\"\n",
     358            0 :                                                         blocker->stepname, pstep->name);
     359            0 :                                         exit(1);
     360              :                                 }
     361              :                                 /* can't block on completion of step of own session */
     362            0 :                                 if (blocker->step->session == pstep->step->session)
     363              :                                 {
     364            0 :                                         fprintf(stderr, "permutation step \"%s\" cannot block on its own session\n",
     365            0 :                                                         pstep->name);
     366            0 :                                         exit(1);
     367              :                                 }
     368            0 :                         }
     369            0 :                 }
     370            0 :         }
     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            0 :         if (testspec->permutations)
     378              :         {
     379            0 :                 for (i = 0; i < nallsteps; i++)
     380              :                 {
     381            0 :                         if (!allsteps[i]->used)
     382            0 :                                 fprintf(stderr, "unused step name: %s\n", allsteps[i]->name);
     383            0 :                 }
     384            0 :         }
     385              : 
     386            0 :         free(allsteps);
     387            0 : }
     388              : 
     389              : /*
     390              :  * Run the permutations specified in the spec, or all if none were
     391              :  * explicitly specified.
     392              :  */
     393              : static void
     394            0 : run_testspec(TestSpec *testspec)
     395              : {
     396            0 :         if (testspec->permutations)
     397            0 :                 run_named_permutations(testspec);
     398              :         else
     399            0 :                 run_all_permutations(testspec);
     400            0 : }
     401              : 
     402              : /*
     403              :  * Run all permutations of the steps and sessions.
     404              :  */
     405              : static void
     406            0 : run_all_permutations(TestSpec *testspec)
     407              : {
     408            0 :         int                     nsteps;
     409            0 :         int                     i;
     410            0 :         PermutationStep *steps;
     411            0 :         PermutationStep **stepptrs;
     412            0 :         int                *piles;
     413              : 
     414              :         /* Count the total number of steps in all sessions */
     415            0 :         nsteps = 0;
     416            0 :         for (i = 0; i < testspec->nsessions; i++)
     417            0 :                 nsteps += testspec->sessions[i]->nsteps;
     418              : 
     419              :         /* Create PermutationStep workspace array */
     420            0 :         steps = (PermutationStep *) pg_malloc0(sizeof(PermutationStep) * nsteps);
     421            0 :         stepptrs = (PermutationStep **) pg_malloc(sizeof(PermutationStep *) * nsteps);
     422            0 :         for (i = 0; i < nsteps; i++)
     423            0 :                 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            0 :         piles = pg_malloc(sizeof(int) * testspec->nsessions);
     435            0 :         for (i = 0; i < testspec->nsessions; i++)
     436            0 :                 piles[i] = 0;
     437              : 
     438            0 :         run_all_permutations_recurse(testspec, piles, 0, stepptrs);
     439              : 
     440            0 :         free(steps);
     441            0 :         free(stepptrs);
     442            0 :         free(piles);
     443            0 : }
     444              : 
     445              : static void
     446            0 : run_all_permutations_recurse(TestSpec *testspec, int *piles,
     447              :                                                          int nsteps, PermutationStep **steps)
     448              : {
     449            0 :         int                     i;
     450            0 :         bool            found = false;
     451              : 
     452            0 :         for (i = 0; i < testspec->nsessions; i++)
     453              :         {
     454              :                 /* If there's any more steps in this pile, pick it and recurse */
     455            0 :                 if (piles[i] < testspec->sessions[i]->nsteps)
     456              :                 {
     457            0 :                         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            0 :                         steps[nsteps]->name = newstep->name;
     465            0 :                         steps[nsteps]->step = newstep;
     466              : 
     467            0 :                         piles[i]++;
     468              : 
     469            0 :                         run_all_permutations_recurse(testspec, piles, nsteps + 1, steps);
     470              : 
     471            0 :                         piles[i]--;
     472              : 
     473            0 :                         found = true;
     474            0 :                 }
     475            0 :         }
     476              : 
     477              :         /* If all the piles were empty, this permutation is completed. Run it */
     478            0 :         if (!found)
     479            0 :                 run_permutation(testspec, nsteps, steps);
     480            0 : }
     481              : 
     482              : /*
     483              :  * Run permutations given in the test spec
     484              :  */
     485              : static void
     486            0 : run_named_permutations(TestSpec *testspec)
     487              : {
     488            0 :         int                     i;
     489              : 
     490            0 :         for (i = 0; i < testspec->npermutations; i++)
     491              :         {
     492            0 :                 Permutation *p = testspec->permutations[i];
     493              : 
     494            0 :                 run_permutation(testspec, p->nsteps, p->steps);
     495            0 :         }
     496            0 : }
     497              : 
     498              : static int
     499            0 : step_qsort_cmp(const void *a, const void *b)
     500              : {
     501            0 :         Step       *stepa = *((Step **) a);
     502            0 :         Step       *stepb = *((Step **) b);
     503              : 
     504            0 :         return strcmp(stepa->name, stepb->name);
     505            0 : }
     506              : 
     507              : static int
     508            0 : step_bsearch_cmp(const void *a, const void *b)
     509              : {
     510            0 :         char       *stepname = (char *) a;
     511            0 :         Step       *step = *((Step **) b);
     512              : 
     513            0 :         return strcmp(stepname, step->name);
     514            0 : }
     515              : 
     516              : /*
     517              :  * Run one permutation
     518              :  */
     519              : static void
     520            0 : run_permutation(TestSpec *testspec, int nsteps, PermutationStep **steps)
     521              : {
     522            0 :         PGresult   *res;
     523            0 :         int                     i;
     524            0 :         int                     nwaiting = 0;
     525            0 :         PermutationStep **waiting;
     526              : 
     527            0 :         waiting = pg_malloc(sizeof(PermutationStep *) * testspec->nsessions);
     528              : 
     529            0 :         printf("\nstarting permutation:");
     530            0 :         for (i = 0; i < nsteps; i++)
     531            0 :                 printf(" %s", steps[i]->name);
     532            0 :         printf("\n");
     533              : 
     534              :         /* Perform setup */
     535            0 :         for (i = 0; i < testspec->nsetupsqls; i++)
     536              :         {
     537            0 :                 res = PQexec(conns[0].conn, testspec->setupsqls[i]);
     538            0 :                 if (PQresultStatus(res) == PGRES_TUPLES_OK)
     539              :                 {
     540            0 :                         printResultSet(res);
     541            0 :                 }
     542            0 :                 else if (PQresultStatus(res) != PGRES_COMMAND_OK)
     543              :                 {
     544            0 :                         fprintf(stderr, "setup failed: %s", PQerrorMessage(conns[0].conn));
     545            0 :                         exit(1);
     546              :                 }
     547            0 :                 PQclear(res);
     548            0 :         }
     549              : 
     550              :         /* Perform per-session setup */
     551            0 :         for (i = 0; i < testspec->nsessions; i++)
     552              :         {
     553            0 :                 if (testspec->sessions[i]->setupsql)
     554              :                 {
     555            0 :                         res = PQexec(conns[i + 1].conn, testspec->sessions[i]->setupsql);
     556            0 :                         if (PQresultStatus(res) == PGRES_TUPLES_OK)
     557              :                         {
     558            0 :                                 printResultSet(res);
     559            0 :                         }
     560            0 :                         else if (PQresultStatus(res) != PGRES_COMMAND_OK)
     561              :                         {
     562            0 :                                 fprintf(stderr, "setup of session %s failed: %s",
     563            0 :                                                 conns[i + 1].sessionname,
     564            0 :                                                 PQerrorMessage(conns[i + 1].conn));
     565            0 :                                 exit(1);
     566              :                         }
     567            0 :                         PQclear(res);
     568            0 :                 }
     569            0 :         }
     570              : 
     571              :         /* Perform steps */
     572            0 :         for (i = 0; i < nsteps; i++)
     573              :         {
     574            0 :                 PermutationStep *pstep = steps[i];
     575            0 :                 Step       *step = pstep->step;
     576            0 :                 IsoConnInfo *iconn = &conns[1 + step->session];
     577            0 :                 PGconn     *conn = iconn->conn;
     578            0 :                 bool            mustwait;
     579            0 :                 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            0 :                 if (iconn->active_step != NULL)
     586              :                 {
     587            0 :                         struct timeval start_time;
     588              : 
     589            0 :                         gettimeofday(&start_time, NULL);
     590              : 
     591            0 :                         while (iconn->active_step != NULL)
     592              :                         {
     593            0 :                                 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            0 :                                 if (!try_complete_step(testspec, oldstep, STEP_RETRY))
     601              :                                 {
     602              :                                         /* Done, so remove oldstep from the waiting[] array. */
     603            0 :                                         int                     w;
     604              : 
     605            0 :                                         for (w = 0; w < nwaiting; w++)
     606              :                                         {
     607            0 :                                                 if (oldstep == waiting[w])
     608            0 :                                                         break;
     609            0 :                                         }
     610            0 :                                         if (w >= nwaiting)
     611            0 :                                                 abort();        /* can't happen */
     612            0 :                                         if (w + 1 < nwaiting)
     613            0 :                                                 memmove(&waiting[w], &waiting[w + 1],
     614              :                                                                 (nwaiting - (w + 1)) * sizeof(PermutationStep *));
     615            0 :                                         nwaiting--;
     616            0 :                                 }
     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            0 :                                 nwaiting = try_complete_steps(testspec, waiting, nwaiting,
     626              :                                                                                           STEP_NONBLOCK | STEP_RETRY);
     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            0 :                                 if (iconn->active_step != NULL)
     637              :                                 {
     638            0 :                                         struct timeval current_time;
     639            0 :                                         int64           td;
     640              : 
     641            0 :                                         gettimeofday(&current_time, NULL);
     642            0 :                                         td = (int64) current_time.tv_sec - (int64) start_time.tv_sec;
     643            0 :                                         td *= USECS_PER_SEC;
     644            0 :                                         td += (int64) current_time.tv_usec - (int64) start_time.tv_usec;
     645            0 :                                         if (td > 2 * max_step_wait)
     646              :                                         {
     647            0 :                                                 fprintf(stderr, "step %s timed out after %d seconds\n",
     648            0 :                                                                 iconn->active_step->name,
     649            0 :                                                                 (int) (td / USECS_PER_SEC));
     650            0 :                                                 fprintf(stderr, "active steps are:");
     651            0 :                                                 for (j = 1; j < nconns; j++)
     652              :                                                 {
     653            0 :                                                         IsoConnInfo *oconn = &conns[j];
     654              : 
     655            0 :                                                         if (oconn->active_step != NULL)
     656            0 :                                                                 fprintf(stderr, " %s",
     657            0 :                                                                                 oconn->active_step->name);
     658            0 :                                                 }
     659            0 :                                                 fprintf(stderr, "\n");
     660            0 :                                                 exit(1);
     661              :                                         }
     662            0 :                                 }
     663            0 :                         }
     664            0 :                 }
     665              : 
     666              :                 /* Send the query for this step. */
     667            0 :                 if (!PQsendQuery(conn, step->sql))
     668              :                 {
     669            0 :                         fprintf(stdout, "failed to send query for step %s: %s\n",
     670            0 :                                         step->name, PQerrorMessage(conn));
     671            0 :                         exit(1);
     672              :                 }
     673              : 
     674              :                 /* Remember we launched a step. */
     675            0 :                 iconn->active_step = pstep;
     676              : 
     677              :                 /* Remember target number of NOTICEs for any blocker conditions. */
     678            0 :                 for (j = 0; j < pstep->nblockers; j++)
     679              :                 {
     680            0 :                         PermutationStepBlocker *blocker = pstep->blockers[j];
     681              : 
     682            0 :                         if (blocker->blocktype == PSB_NUM_NOTICES)
     683            0 :                                 blocker->target_notices = blocker->num_notices +
     684            0 :                                         conns[blocker->step->session + 1].total_notices;
     685            0 :                 }
     686              : 
     687              :                 /* Try to complete this step without blocking.  */
     688            0 :                 mustwait = try_complete_step(testspec, pstep, STEP_NONBLOCK);
     689              : 
     690              :                 /* Check for completion of any steps that were previously waiting. */
     691            0 :                 nwaiting = try_complete_steps(testspec, waiting, nwaiting,
     692              :                                                                           STEP_NONBLOCK | STEP_RETRY);
     693              : 
     694              :                 /* If this step is waiting, add it to the array of waiters. */
     695            0 :                 if (mustwait)
     696            0 :                         waiting[nwaiting++] = pstep;
     697            0 :         }
     698              : 
     699              :         /* Wait for any remaining queries. */
     700            0 :         nwaiting = try_complete_steps(testspec, waiting, nwaiting, STEP_RETRY);
     701            0 :         if (nwaiting != 0)
     702              :         {
     703            0 :                 fprintf(stderr, "failed to complete permutation due to mutually-blocking steps\n");
     704            0 :                 exit(1);
     705              :         }
     706              : 
     707              :         /* Perform per-session teardown */
     708            0 :         for (i = 0; i < testspec->nsessions; i++)
     709              :         {
     710            0 :                 if (testspec->sessions[i]->teardownsql)
     711              :                 {
     712            0 :                         res = PQexec(conns[i + 1].conn, testspec->sessions[i]->teardownsql);
     713            0 :                         if (PQresultStatus(res) == PGRES_TUPLES_OK)
     714              :                         {
     715            0 :                                 printResultSet(res);
     716            0 :                         }
     717            0 :                         else if (PQresultStatus(res) != PGRES_COMMAND_OK)
     718              :                         {
     719            0 :                                 fprintf(stderr, "teardown of session %s failed: %s",
     720            0 :                                                 conns[i + 1].sessionname,
     721            0 :                                                 PQerrorMessage(conns[i + 1].conn));
     722              :                                 /* don't exit on teardown failure */
     723            0 :                         }
     724            0 :                         PQclear(res);
     725            0 :                 }
     726            0 :         }
     727              : 
     728              :         /* Perform teardown */
     729            0 :         if (testspec->teardownsql)
     730              :         {
     731            0 :                 res = PQexec(conns[0].conn, testspec->teardownsql);
     732            0 :                 if (PQresultStatus(res) == PGRES_TUPLES_OK)
     733              :                 {
     734            0 :                         printResultSet(res);
     735            0 :                 }
     736            0 :                 else if (PQresultStatus(res) != PGRES_COMMAND_OK)
     737              :                 {
     738            0 :                         fprintf(stderr, "teardown failed: %s",
     739            0 :                                         PQerrorMessage(conns[0].conn));
     740              :                         /* don't exit on teardown failure */
     741            0 :                 }
     742            0 :                 PQclear(res);
     743            0 :         }
     744              : 
     745            0 :         free(waiting);
     746            0 : }
     747              : 
     748              : /*
     749              :  * Check for completion of any waiting step(s).
     750              :  * Remove completed ones from the waiting[] array,
     751              :  * and return the new value of nwaiting.
     752              :  * See try_complete_step for the meaning of the flags.
     753              :  */
     754              : static int
     755            0 : try_complete_steps(TestSpec *testspec, PermutationStep **waiting,
     756              :                                    int nwaiting, int flags)
     757              : {
     758            0 :         int                     old_nwaiting;
     759            0 :         bool            have_blocker;
     760              : 
     761            0 :         do
     762              :         {
     763            0 :                 int                     w = 0;
     764              : 
     765              :                 /* Reset latch; we only care about notices received within loop. */
     766            0 :                 any_new_notice = false;
     767              : 
     768              :                 /* Likewise, these variables reset for each retry. */
     769            0 :                 old_nwaiting = nwaiting;
     770            0 :                 have_blocker = false;
     771              : 
     772              :                 /* Scan the array, try to complete steps. */
     773            0 :                 while (w < nwaiting)
     774              :                 {
     775            0 :                         if (try_complete_step(testspec, waiting[w], flags))
     776              :                         {
     777              :                                 /* Still blocked, leave it alone. */
     778            0 :                                 if (waiting[w]->nblockers > 0)
     779            0 :                                         have_blocker = true;
     780            0 :                                 w++;
     781            0 :                         }
     782              :                         else
     783              :                         {
     784              :                                 /* Done, remove it from array. */
     785            0 :                                 if (w + 1 < nwaiting)
     786            0 :                                         memmove(&waiting[w], &waiting[w + 1],
     787              :                                                         (nwaiting - (w + 1)) * sizeof(PermutationStep *));
     788            0 :                                 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            0 :         } while (have_blocker && (nwaiting < old_nwaiting || any_new_notice));
     801            0 :         return nwaiting;
     802            0 : }
     803              : 
     804              : /*
     805              :  * Our caller already sent the query associated with this step.  Wait for it
     806              :  * to either complete, or hit a blocking condition.
     807              :  *
     808              :  * When calling this function on behalf of a given step for a second or later
     809              :  * time, pass the STEP_RETRY flag.  Do not pass it on the first call.
     810              :  *
     811              :  * Returns true if the step was *not* completed, false if it was completed.
     812              :  * Reasons for non-completion are (a) the STEP_NONBLOCK flag was specified
     813              :  * and the query is waiting to acquire a lock, or (b) the step has an
     814              :  * unsatisfied blocker condition.  When STEP_NONBLOCK is given, we assume
     815              :  * that any lock wait will persist until we have executed additional steps.
     816              :  */
     817              : static bool
     818            0 : try_complete_step(TestSpec *testspec, PermutationStep *pstep, int flags)
     819              : {
     820            0 :         Step       *step = pstep->step;
     821            0 :         IsoConnInfo *iconn = &conns[1 + step->session];
     822            0 :         PGconn     *conn = iconn->conn;
     823            0 :         fd_set          read_set;
     824            0 :         struct timeval start_time;
     825            0 :         struct timeval timeout;
     826            0 :         int                     sock = PQsocket(conn);
     827            0 :         int                     ret;
     828            0 :         PGresult   *res;
     829            0 :         PGnotify   *notify;
     830            0 :         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            0 :         if (!(flags & STEP_RETRY))
     838              :         {
     839            0 :                 int                     i;
     840              : 
     841            0 :                 for (i = 0; i < pstep->nblockers; i++)
     842              :                 {
     843            0 :                         PermutationStepBlocker *blocker = pstep->blockers[i];
     844              : 
     845            0 :                         if (blocker->blocktype == PSB_ONCE)
     846              :                         {
     847            0 :                                 printf("step %s: %s <waiting ...>\n",
     848              :                                            step->name, step->sql);
     849            0 :                                 return true;
     850              :                         }
     851            0 :                 }
     852            0 :         }
     853              : 
     854            0 :         if (sock < 0)
     855              :         {
     856            0 :                 fprintf(stderr, "invalid socket: %s", PQerrorMessage(conn));
     857            0 :                 exit(1);
     858              :         }
     859              : 
     860            0 :         gettimeofday(&start_time, NULL);
     861            0 :         FD_ZERO(&read_set);
     862              : 
     863            0 :         while (PQisBusy(conn))
     864              :         {
     865            0 :                 FD_SET(sock, &read_set);
     866            0 :                 timeout.tv_sec = 0;
     867            0 :                 timeout.tv_usec = 10000;        /* Check for lock waits every 10ms. */
     868              : 
     869            0 :                 ret = select(sock + 1, &read_set, NULL, NULL, &timeout);
     870            0 :                 if (ret < 0)                 /* error in select() */
     871              :                 {
     872            0 :                         if (errno == EINTR)
     873            0 :                                 continue;
     874            0 :                         fprintf(stderr, "select failed: %m\n");
     875            0 :                         exit(1);
     876              :                 }
     877            0 :                 else if (ret == 0)              /* select() timeout: check for lock wait */
     878              :                 {
     879            0 :                         struct timeval current_time;
     880            0 :                         int64           td;
     881              : 
     882              :                         /* If it's OK for the step to block, check whether it has. */
     883            0 :                         if (flags & STEP_NONBLOCK)
     884              :                         {
     885            0 :                                 bool            waiting;
     886              : 
     887            0 :                                 res = PQexecPrepared(conns[0].conn, PREP_WAITING, 1,
     888            0 :                                                                          &conns[step->session + 1].backend_pid_str,
     889              :                                                                          NULL, NULL, 0);
     890            0 :                                 if (PQresultStatus(res) != PGRES_TUPLES_OK ||
     891            0 :                                         PQntuples(res) != 1)
     892              :                                 {
     893            0 :                                         fprintf(stderr, "lock wait query failed: %s",
     894            0 :                                                         PQerrorMessage(conns[0].conn));
     895            0 :                                         exit(1);
     896              :                                 }
     897            0 :                                 waiting = ((PQgetvalue(res, 0, 0))[0] == 't');
     898            0 :                                 PQclear(res);
     899              : 
     900            0 :                                 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            0 :                                         if (!PQconsumeInput(conn))
     912              :                                         {
     913            0 :                                                 fprintf(stderr, "PQconsumeInput failed: %s\n",
     914            0 :                                                                 PQerrorMessage(conn));
     915            0 :                                                 exit(1);
     916              :                                         }
     917            0 :                                         if (!PQisBusy(conn))
     918            0 :                                                 break;
     919              : 
     920              :                                         /*
     921              :                                          * conn is still busy, so conclude that the step really is
     922              :                                          * waiting.
     923              :                                          */
     924            0 :                                         if (!(flags & STEP_RETRY))
     925            0 :                                                 printf("step %s: %s <waiting ...>\n",
     926              :                                                            step->name, step->sql);
     927            0 :                                         return true;
     928              :                                 }
     929              :                                 /* else, not waiting */
     930            0 :                         }
     931              : 
     932              :                         /* Figure out how long we've been waiting for this step. */
     933            0 :                         gettimeofday(&current_time, NULL);
     934            0 :                         td = (int64) current_time.tv_sec - (int64) start_time.tv_sec;
     935            0 :                         td *= USECS_PER_SEC;
     936            0 :                         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            0 :                         if (td > max_step_wait && !canceled)
     948              :                         {
     949            0 :                                 PGcancelConn *cancel_conn = PQcancelCreate(conn);
     950              : 
     951            0 :                                 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            0 :                                         printf("isolationtester: canceling step %s after %d seconds\n",
     958              :                                                    step->name, (int) (td / USECS_PER_SEC));
     959            0 :                                         canceled = true;
     960            0 :                                 }
     961              :                                 else
     962            0 :                                         fprintf(stderr, "PQcancel failed: %s\n", PQcancelErrorMessage(cancel_conn));
     963            0 :                                 PQcancelFinish(cancel_conn);
     964            0 :                         }
     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            0 :                         if (td > 2 * max_step_wait)
     974              :                         {
     975            0 :                                 fprintf(stderr, "step %s timed out after %d seconds\n",
     976            0 :                                                 step->name, (int) (td / USECS_PER_SEC));
     977            0 :                                 exit(1);
     978              :                         }
     979            0 :                 }
     980            0 :                 else if (!PQconsumeInput(conn)) /* select(): data available */
     981              :                 {
     982            0 :                         fprintf(stderr, "PQconsumeInput failed: %s\n",
     983            0 :                                         PQerrorMessage(conn));
     984            0 :                         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            0 :         if (step_has_blocker(pstep))
     993              :         {
     994            0 :                 if (!(flags & STEP_RETRY))
     995            0 :                         printf("step %s: %s <waiting ...>\n",
     996              :                                    step->name, step->sql);
     997            0 :                 return true;
     998              :         }
     999              : 
    1000              :         /* Otherwise, go ahead and complete it. */
    1001            0 :         if (flags & STEP_RETRY)
    1002            0 :                 printf("step %s: <... completed>\n", step->name);
    1003              :         else
    1004            0 :                 printf("step %s: %s\n", step->name, step->sql);
    1005              : 
    1006            0 :         while ((res = PQgetResult(conn)))
    1007              :         {
    1008            0 :                 switch (PQresultStatus(res))
    1009              :                 {
    1010              :                         case PGRES_COMMAND_OK:
    1011              :                         case PGRES_EMPTY_QUERY:
    1012            0 :                                 break;
    1013              :                         case PGRES_TUPLES_OK:
    1014            0 :                                 printResultSet(res);
    1015            0 :                                 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            0 :                                         const char *sev = PQresultErrorField(res,
    1025              :                                                                                                                  PG_DIAG_SEVERITY);
    1026            0 :                                         const char *msg = PQresultErrorField(res,
    1027              :                                                                                                                  PG_DIAG_MESSAGE_PRIMARY);
    1028              : 
    1029            0 :                                         if (sev && msg)
    1030            0 :                                                 printf("%s:  %s\n", sev, msg);
    1031              :                                         else
    1032            0 :                                                 printf("%s\n", PQresultErrorMessage(res));
    1033            0 :                                 }
    1034            0 :                                 break;
    1035              :                         default:
    1036            0 :                                 printf("unexpected result status: %s\n",
    1037              :                                            PQresStatus(PQresultStatus(res)));
    1038            0 :                 }
    1039            0 :                 PQclear(res);
    1040              :         }
    1041              : 
    1042              :         /* Report any available NOTIFY messages, too */
    1043            0 :         PQconsumeInput(conn);
    1044            0 :         while ((notify = PQnotifies(conn)) != NULL)
    1045              :         {
    1046              :                 /* Try to identify which session it came from */
    1047            0 :                 const char *sendername = NULL;
    1048            0 :                 char            pidstring[32];
    1049            0 :                 int                     i;
    1050              : 
    1051            0 :                 for (i = 0; i < testspec->nsessions; i++)
    1052              :                 {
    1053            0 :                         if (notify->be_pid == conns[i + 1].backend_pid)
    1054              :                         {
    1055            0 :                                 sendername = conns[i + 1].sessionname;
    1056            0 :                                 break;
    1057              :                         }
    1058            0 :                 }
    1059            0 :                 if (sendername == NULL)
    1060              :                 {
    1061              :                         /* Doesn't seem to be any test session, so show the hard way */
    1062            0 :                         snprintf(pidstring, sizeof(pidstring), "PID %d", notify->be_pid);
    1063            0 :                         sendername = pidstring;
    1064            0 :                 }
    1065            0 :                 printf("%s: NOTIFY \"%s\" with payload \"%s\" from %s\n",
    1066              :                            testspec->sessions[step->session]->name,
    1067              :                            notify->relname, notify->extra, sendername);
    1068            0 :                 PQfreemem(notify);
    1069            0 :                 PQconsumeInput(conn);
    1070            0 :         }
    1071              : 
    1072              :         /* Connection is now idle. */
    1073            0 :         iconn->active_step = NULL;
    1074              : 
    1075            0 :         return false;
    1076            0 : }
    1077              : 
    1078              : /* Detect whether a step has any unsatisfied blocker conditions */
    1079              : static bool
    1080            0 : step_has_blocker(PermutationStep *pstep)
    1081              : {
    1082            0 :         int                     i;
    1083              : 
    1084            0 :         for (i = 0; i < pstep->nblockers; i++)
    1085              :         {
    1086            0 :                 PermutationStepBlocker *blocker = pstep->blockers[i];
    1087            0 :                 IsoConnInfo *iconn;
    1088              : 
    1089            0 :                 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            0 :                                 iconn = &conns[1 + blocker->step->session];
    1097            0 :                                 if (iconn->active_step &&
    1098            0 :                                         iconn->active_step->step == blocker->step)
    1099            0 :                                         return true;
    1100            0 :                                 break;
    1101              :                         case PSB_NUM_NOTICES:
    1102              :                                 /* Block if not enough notices received yet */
    1103            0 :                                 iconn = &conns[1 + blocker->step->session];
    1104            0 :                                 if (iconn->total_notices < blocker->target_notices)
    1105            0 :                                         return true;
    1106            0 :                                 break;
    1107              :                 }
    1108            0 :         }
    1109            0 :         return false;
    1110            0 : }
    1111              : 
    1112              : static void
    1113            0 : printResultSet(PGresult *res)
    1114              : {
    1115            0 :         PQprintOpt      popt;
    1116              : 
    1117            0 :         memset(&popt, 0, sizeof(popt));
    1118            0 :         popt.header = true;
    1119            0 :         popt.align = true;
    1120            0 :         popt.fieldSep = "|";
    1121            0 :         PQprint(stdout, res, &popt);
    1122            0 : }
    1123              : 
    1124              : /* notice processor for regular user sessions */
    1125              : static void
    1126            0 : isotesterNoticeProcessor(void *arg, const char *message)
    1127              : {
    1128            0 :         IsoConnInfo *myconn = (IsoConnInfo *) arg;
    1129              : 
    1130              :         /* Prefix the backend's message with the session name. */
    1131            0 :         printf("%s: %s", myconn->sessionname, message);
    1132              :         /* Record notices, since we may need this to decide to unblock a step. */
    1133            0 :         myconn->total_notices++;
    1134            0 :         any_new_notice = true;
    1135            0 : }
    1136              : 
    1137              : /* notice processor, hides the message */
    1138              : static void
    1139            0 : blackholeNoticeProcessor(void *arg, const char *message)
    1140              : {
    1141              :         /* do nothing */
    1142            0 : }
        

Generated by: LCOV version 2.3.2-1