Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Functions | Variables

isolationtester.c File Reference

#include "postgres_fe.h"
#include <sys/time.h>
#include <unistd.h>
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include "isolationtester.h"
Include dependency graph for isolationtester.c:

Go to the source code of this file.

Defines

#define PREP_WAITING   "isolationtester_waiting"
#define STEP_NONBLOCK   0x1
#define STEP_RETRY   0x2

Functions

static void run_testspec (TestSpec *testspec)
static void run_all_permutations (TestSpec *testspec)
static void run_all_permutations_recurse (TestSpec *testspec, int nsteps, Step **steps)
static void run_named_permutations (TestSpec *testspec)
static void run_permutation (TestSpec *testspec, int nsteps, Step **steps)
static bool try_complete_step (Step *step, int flags)
static int step_qsort_cmp (const void *a, const void *b)
static int step_bsearch_cmp (const void *a, const void *b)
static void printResultSet (PGresult *res)
static void exit_nicely (void)
int main (int argc, char **argv)
static void report_error_message (Step *step)
static void report_two_error_messages (Step *step1, Step *step2)

Variables

int optind
static PGconn ** conns = NULL
static const char ** backend_pids = NULL
static int nconns = 0
static int dry_run = false
static int * piles

Define Documentation

#define PREP_WAITING   "isolationtester_waiting"

Definition at line 36 of file isolationtester.c.

Referenced by main(), and try_complete_step().

#define STEP_NONBLOCK   0x1

Definition at line 56 of file isolationtester.c.

Referenced by run_permutation(), and try_complete_step().

#define STEP_RETRY   0x2

Definition at line 57 of file isolationtester.c.

Referenced by run_permutation(), and try_complete_step().


Function Documentation

static void exit_nicely ( void   )  [static]

Definition at line 67 of file isolationtester.c.

References i, nconns, and PQfinish().

{
    int         i;

    for (i = 0; i < nconns; i++)
        PQfinish(conns[i]);
    fflush(stderr);
    fflush(stdout);
    exit(1);
}

int main ( int  argc,
char **  argv 
)

Definition at line 79 of file isolationtester.c.

References appendPQExpBuffer(), appendPQExpBufferStr(), backend_pids, calloc, CONNECTION_OK, PQExpBufferData::data, dry_run, exit_nicely, getopt(), i, initPQExpBuffer(), nconns, TestSpec::nsessions, Session::nsteps, NULL, optind, parseresult, PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQconnectdb(), PQerrorMessage(), PQexec(), PQfinish(), PQgetvalue(), PQnfields(), PQntuples(), PQprepare(), PQresultStatus(), PQstatus(), PREP_WAITING, run_testspec(), Step::session, TestSpec::sessions, spec_yyparse(), Session::steps, and termPQExpBuffer().

{
    const char *conninfo;
    TestSpec   *testspec;
    int         i;
    PGresult   *res;
    PQExpBufferData wait_query;
    int         opt;

    while ((opt = getopt(argc, argv, "n")) != -1)
    {
        switch (opt)
        {
            case 'n':
                dry_run = true;
                break;
            default:
                fprintf(stderr, "Usage: isolationtester [-n] [CONNINFO]\n");
                return EXIT_FAILURE;
        }
    }

    /*
     * If the user supplies a non-option parameter on the command line, use it
     * as the conninfo string; otherwise default to setting dbname=postgres
     * and using environment variables or defaults for all other connection
     * parameters.
     */
    if (argc > optind)
        conninfo = argv[optind];
    else
        conninfo = "dbname = postgres";

    /* Read the test spec from stdin */
    spec_yyparse();
    testspec = &parseresult;

    /*
     * In dry-run mode, just print the permutations that would be run, and
     * exit.
     */
    if (dry_run)
    {
        run_testspec(testspec);
        return 0;
    }

    printf("Parsed test spec with %d sessions\n", testspec->nsessions);

    /*
     * Establish connections to the database, one for each session and an
     * extra for lock wait detection and global work.
     */
    nconns = 1 + testspec->nsessions;
    conns = calloc(nconns, sizeof(PGconn *));
    backend_pids = calloc(nconns, sizeof(*backend_pids));
    for (i = 0; i < nconns; i++)
    {
        conns[i] = PQconnectdb(conninfo);
        if (PQstatus(conns[i]) != CONNECTION_OK)
        {
            fprintf(stderr, "Connection %d to database failed: %s",
                    i, PQerrorMessage(conns[i]));
            exit_nicely();
        }

        /*
         * Suppress NOTIFY messages, which otherwise pop into results at odd
         * places.
         */
        res = PQexec(conns[i], "SET client_min_messages = warning;");
        if (PQresultStatus(res) != PGRES_COMMAND_OK)
        {
            fprintf(stderr, "message level setup failed: %s", PQerrorMessage(conns[i]));
            exit_nicely();
        }
        PQclear(res);

        /* Get the backend pid for lock wait checking. */
        res = PQexec(conns[i], "SELECT pg_backend_pid()");
        if (PQresultStatus(res) == PGRES_TUPLES_OK)
        {
            if (PQntuples(res) == 1 && PQnfields(res) == 1)
                backend_pids[i] = strdup(PQgetvalue(res, 0, 0));
            else
            {
                fprintf(stderr, "backend pid query returned %d rows and %d columns, expected 1 row and 1 column",
                        PQntuples(res), PQnfields(res));
                exit_nicely();
            }
        }
        else
        {
            fprintf(stderr, "backend pid query failed: %s",
                    PQerrorMessage(conns[i]));
            exit_nicely();
        }
        PQclear(res);
    }

    /* Set the session index fields in steps. */
    for (i = 0; i < testspec->nsessions; i++)
    {
        Session    *session = testspec->sessions[i];
        int         stepindex;

        for (stepindex = 0; stepindex < session->nsteps; stepindex++)
            session->steps[stepindex]->session = i;
    }

    /*
     * Build the query we'll use to detect lock contention among sessions in
     * the test specification.  Most of the time, we could get away with
     * simply checking whether a session is waiting for *any* lock: we don't
     * exactly expect concurrent use of test tables.  However, autovacuum will
     * occasionally take AccessExclusiveLock to truncate a table, and we must
     * ignore that transient wait.
     */
    initPQExpBuffer(&wait_query);
    appendPQExpBufferStr(&wait_query,
                         "SELECT 1 FROM pg_locks holder, pg_locks waiter "
                         "WHERE NOT waiter.granted AND waiter.pid = $1 "
                         "AND holder.granted "
                         "AND holder.pid <> $1 AND holder.pid IN (");
    /* The spec syntax requires at least one session; assume that here. */
    appendPQExpBuffer(&wait_query, "%s", backend_pids[1]);
    for (i = 2; i < nconns; i++)
        appendPQExpBuffer(&wait_query, ", %s", backend_pids[i]);
    appendPQExpBufferStr(&wait_query,
                         ") "

                         "AND holder.mode = ANY (CASE waiter.mode "
                         "WHEN 'AccessShareLock' THEN ARRAY["
                         "'AccessExclusiveLock'] "
                         "WHEN 'RowShareLock' THEN ARRAY["
                         "'ExclusiveLock',"
                         "'AccessExclusiveLock'] "
                         "WHEN 'RowExclusiveLock' THEN ARRAY["
                         "'ShareLock',"
                         "'ShareRowExclusiveLock',"
                         "'ExclusiveLock',"
                         "'AccessExclusiveLock'] "
                         "WHEN 'ShareUpdateExclusiveLock' THEN ARRAY["
                         "'ShareUpdateExclusiveLock',"
                         "'ShareLock',"
                         "'ShareRowExclusiveLock',"
                         "'ExclusiveLock',"
                         "'AccessExclusiveLock'] "
                         "WHEN 'ShareLock' THEN ARRAY["
                         "'RowExclusiveLock',"
                         "'ShareUpdateExclusiveLock',"
                         "'ShareRowExclusiveLock',"
                         "'ExclusiveLock',"
                         "'AccessExclusiveLock'] "
                         "WHEN 'ShareRowExclusiveLock' THEN ARRAY["
                         "'RowExclusiveLock',"
                         "'ShareUpdateExclusiveLock',"
                         "'ShareLock',"
                         "'ShareRowExclusiveLock',"
                         "'ExclusiveLock',"
                         "'AccessExclusiveLock'] "
                         "WHEN 'ExclusiveLock' THEN ARRAY["
                         "'RowShareLock',"
                         "'RowExclusiveLock',"
                         "'ShareUpdateExclusiveLock',"
                         "'ShareLock',"
                         "'ShareRowExclusiveLock',"
                         "'ExclusiveLock',"
                         "'AccessExclusiveLock'] "
                         "WHEN 'AccessExclusiveLock' THEN ARRAY["
                         "'AccessShareLock',"
                         "'RowShareLock',"
                         "'RowExclusiveLock',"
                         "'ShareUpdateExclusiveLock',"
                         "'ShareLock',"
                         "'ShareRowExclusiveLock',"
                         "'ExclusiveLock',"
                         "'AccessExclusiveLock'] END) "

                  "AND holder.locktype IS NOT DISTINCT FROM waiter.locktype "
                  "AND holder.database IS NOT DISTINCT FROM waiter.database "
                  "AND holder.relation IS NOT DISTINCT FROM waiter.relation "
                         "AND holder.page IS NOT DISTINCT FROM waiter.page "
                         "AND holder.tuple IS NOT DISTINCT FROM waiter.tuple "
              "AND holder.virtualxid IS NOT DISTINCT FROM waiter.virtualxid "
        "AND holder.transactionid IS NOT DISTINCT FROM waiter.transactionid "
                    "AND holder.classid IS NOT DISTINCT FROM waiter.classid "
                         "AND holder.objid IS NOT DISTINCT FROM waiter.objid "
                "AND holder.objsubid IS NOT DISTINCT FROM waiter.objsubid ");

    res = PQprepare(conns[0], PREP_WAITING, wait_query.data, 0, NULL);
    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "prepare of lock wait query failed: %s",
                PQerrorMessage(conns[0]));
        exit_nicely();
    }
    PQclear(res);
    termPQExpBuffer(&wait_query);

    /*
     * Run the permutations specified in the spec, or all if none were
     * explicitly specified.
     */
    run_testspec(testspec);

    /* Clean up and exit */
    for (i = 0; i < nconns; i++)
        PQfinish(conns[i]);
    fflush(stderr);
    fflush(stdout);
    return 0;
}

static void printResultSet ( PGresult res  )  [static]

Definition at line 809 of file isolationtester.c.

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

Referenced by run_permutation(), and try_complete_step().

{
    int         nFields;
    int         i,
                j;

    /* first, print out the attribute names */
    nFields = PQnfields(res);
    for (i = 0; i < nFields; i++)
        printf("%-15s", PQfname(res, i));
    printf("\n\n");

    /* next, print out the rows */
    for (i = 0; i < PQntuples(res); i++)
    {
        for (j = 0; j < nFields; j++)
            printf("%-15s", PQgetvalue(res, i, j));
        printf("\n");
    }
}

static void report_error_message ( Step step  )  [static]

Definition at line 448 of file isolationtester.c.

References Step::errormsg, and free.

Referenced by run_permutation().

{
    if (step->errormsg)
    {
        fprintf(stdout, "%s\n", step->errormsg);
        free(step->errormsg);
        step->errormsg = NULL;
    }
}

static void report_two_error_messages ( Step step1,
Step step2 
) [static]

Definition at line 465 of file isolationtester.c.

References Step::errormsg, free, malloc, and Step::name.

Referenced by run_permutation().

{
    char       *prefix;

    prefix = malloc(strlen(step1->name) + strlen(step2->name) + 2);
    sprintf(prefix, "%s %s", step1->name, step2->name);

    if (step1->errormsg)
    {
        fprintf(stdout, "error in steps %s: %s\n", prefix,
                step1->errormsg);
        free(step1->errormsg);
        step1->errormsg = NULL;
    }
    if (step2->errormsg)
    {
        fprintf(stdout, "error in steps %s: %s\n", prefix,
                step2->errormsg);
        free(step2->errormsg);
        step2->errormsg = NULL;
    }

    free(prefix);
}

static void run_all_permutations ( TestSpec testspec  )  [static]

Definition at line 312 of file isolationtester.c.

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

Referenced by run_testspec().

{
    int         nsteps;
    int         i;
    Step      **steps;

    /* Count the total number of steps in all sessions */
    nsteps = 0;
    for (i = 0; i < testspec->nsessions; i++)
        nsteps += testspec->sessions[i]->nsteps;

    steps = malloc(sizeof(Step *) * nsteps);

    /*
     * To generate the permutations, we conceptually put the steps of each
     * session on a pile. To generate a permutation, we pick steps from the
     * piles until all piles are empty. By picking steps from piles in
     * different order, we get different permutations.
     *
     * A pile is actually just an integer which tells how many steps we've
     * already picked from this pile.
     */
    piles = malloc(sizeof(int) * testspec->nsessions);
    for (i = 0; i < testspec->nsessions; i++)
        piles[i] = 0;

    run_all_permutations_recurse(testspec, 0, steps);
}

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

Definition at line 342 of file isolationtester.c.

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

Referenced by run_all_permutations().

{
    int         i;
    int         found = 0;

    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)
        {
            steps[nsteps] = testspec->sessions[i]->steps[piles[i]];
            piles[i]++;

            run_all_permutations_recurse(testspec, nsteps + 1, steps);

            piles[i]--;

            found = 1;
        }
    }

    /* If all the piles were empty, this permutation is completed. Run it */
    if (!found)
        run_permutation(testspec, nsteps, steps);
}

static void run_named_permutations ( TestSpec testspec  )  [static]

Definition at line 372 of file isolationtester.c.

References exit_nicely, free, i, malloc, TestSpec::npermutations, TestSpec::nsessions, Permutation::nsteps, Session::nsteps, NULL, TestSpec::permutations, qsort, run_permutation(), TestSpec::sessions, step_qsort_cmp(), Permutation::stepnames, and Session::steps.

Referenced by run_testspec().

{
    int         i,
                j;
    int         n;
    int         nallsteps;
    Step      **allsteps;

    /* First create a lookup table of all steps */
    nallsteps = 0;
    for (i = 0; i < testspec->nsessions; i++)
        nallsteps += testspec->sessions[i]->nsteps;

    allsteps = malloc(nallsteps * sizeof(Step *));

    n = 0;
    for (i = 0; i < testspec->nsessions; i++)
    {
        for (j = 0; j < testspec->sessions[i]->nsteps; j++)
            allsteps[n++] = testspec->sessions[i]->steps[j];
    }

    qsort(allsteps, nallsteps, sizeof(Step *), &step_qsort_cmp);

    for (i = 0; i < testspec->npermutations; i++)
    {
        Permutation *p = testspec->permutations[i];
        Step      **steps;

        steps = malloc(p->nsteps * sizeof(Step *));

        /* Find all the named steps using the lookup table */
        for (j = 0; j < p->nsteps; j++)
        {
            Step      **this = (Step **) bsearch(p->stepnames[j], allsteps,
                                                 nallsteps, sizeof(Step *),
                                                 &step_bsearch_cmp);

            if (this == NULL)
            {
                fprintf(stderr, "undefined step \"%s\" specified in permutation\n",
                        p->stepnames[j]);
                exit_nicely();
            }
            steps[j] = *this;
        }

        /* And run them */
        run_permutation(testspec, p->nsteps, steps);

        free(steps);
    }
}

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

Definition at line 494 of file isolationtester.c.

References buf, conn, dry_run, exit_nicely, i, Step::name, Session::name, name, nconns, TestSpec::nsessions, TestSpec::nsetupsqls, NULL, PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQcancel(), PQclear(), PQerrorMessage(), PQexec(), PQfreeCancel(), PQgetCancel(), PQgetResult(), PQresultStatus(), PQsendQuery(), printResultSet(), report_error_message(), report_two_error_messages(), Step::session, TestSpec::sessions, Session::setupsql, TestSpec::setupsqls, Step::sql, STEP_NONBLOCK, STEP_RETRY, TestSpec::teardownsql, Session::teardownsql, try_complete_step(), and waiting.

Referenced by run_all_permutations_recurse(), and run_named_permutations().

{
    PGresult   *res;
    int         i;
    Step       *waiting = NULL;

    /*
     * In dry run mode, just display the permutation in the same format used
     * by spec files, and return.
     */
    if (dry_run)
    {
        printf("permutation");
        for (i = 0; i < nsteps; i++)
            printf(" \"%s\"", steps[i]->name);
        printf("\n");
        return;
    }

    printf("\nstarting permutation:");
    for (i = 0; i < nsteps; i++)
        printf(" %s", steps[i]->name);
    printf("\n");

    /* Perform setup */
    for (i = 0; i < testspec->nsetupsqls; i++)
    {
        res = PQexec(conns[0], testspec->setupsqls[i]);
        if (PQresultStatus(res) != PGRES_COMMAND_OK)
        {
            fprintf(stderr, "setup failed: %s", PQerrorMessage(conns[0]));
            exit_nicely();
        }
        PQclear(res);
    }

    /* Perform per-session setup */
    for (i = 0; i < testspec->nsessions; i++)
    {
        if (testspec->sessions[i]->setupsql)
        {
            res = PQexec(conns[i + 1], testspec->sessions[i]->setupsql);
            if (PQresultStatus(res) == PGRES_TUPLES_OK)
            {
                printResultSet(res);
            }
            else if (PQresultStatus(res) != PGRES_COMMAND_OK)
            {
                fprintf(stderr, "setup of session %s failed: %s",
                        testspec->sessions[i]->name,
                        PQerrorMessage(conns[i + 1]));
                exit_nicely();
            }
            PQclear(res);
        }
    }

    /* Perform steps */
    for (i = 0; i < nsteps; i++)
    {
        Step       *step = steps[i];
        PGconn     *conn = conns[1 + step->session];

        if (waiting != NULL && step->session == waiting->session)
        {
            PGcancel   *cancel;
            PGresult   *res;
            int         j;

            /*
             * This permutation is invalid: it can never happen in real life.
             *
             * A session is blocked on an earlier step (waiting) and no
             * further steps from this session can run until it is unblocked,
             * but it can only be unblocked by running steps from other
             * sessions.
             */
            fflush(stdout);
            fprintf(stderr, "invalid permutation detected\n");
            fflush(stderr);

            /* Cancel the waiting statement from this session. */
            cancel = PQgetCancel(conn);
            if (cancel != NULL)
            {
                char        buf[256];

                if (!PQcancel(cancel, buf, sizeof(buf)))
                    fprintf(stderr, "PQcancel failed: %s\n", buf);

                /* Be sure to consume the error message. */
                while ((res = PQgetResult(conn)) != NULL)
                    PQclear(res);

                PQfreeCancel(cancel);
            }

            /*
             * Now we really have to complete all the running transactions to
             * make sure teardown doesn't block.
             */
            for (j = 1; j < nconns; j++)
            {
                res = PQexec(conns[j], "ROLLBACK");
                if (res != NULL)
                    PQclear(res);
            }

            goto teardown;
        }

        if (!PQsendQuery(conn, step->sql))
        {
            fprintf(stdout, "failed to send query for step %s: %s\n",
                    step->name, PQerrorMessage(conns[1 + step->session]));
            exit_nicely();
        }

        if (waiting != NULL)
        {
            /* Some other step is already waiting: just block. */
            try_complete_step(step, 0);

            /*
             * See if this step unblocked the waiting step; report both error
             * messages together if so.
             */
            if (!try_complete_step(waiting, STEP_NONBLOCK | STEP_RETRY))
            {
                report_two_error_messages(step, waiting);
                waiting = NULL;
            }
            else
                report_error_message(step);
        }
        else
        {
            if (try_complete_step(step, STEP_NONBLOCK))
                waiting = step;
            report_error_message(step);
        }
    }

    /* Finish any waiting query. */
    if (waiting != NULL)
    {
        try_complete_step(waiting, STEP_RETRY);
        report_error_message(waiting);
    }

teardown:
    /* Perform per-session teardown */
    for (i = 0; i < testspec->nsessions; i++)
    {
        if (testspec->sessions[i]->teardownsql)
        {
            res = PQexec(conns[i + 1], testspec->sessions[i]->teardownsql);
            if (PQresultStatus(res) != PGRES_COMMAND_OK)
            {
                fprintf(stderr, "teardown of session %s failed: %s",
                        testspec->sessions[i]->name,
                        PQerrorMessage(conns[i + 1]));
                /* don't exit on teardown failure */
                fflush(stderr);
            }
            PQclear(res);
        }
    }

    /* Perform teardown */
    if (testspec->teardownsql)
    {
        res = PQexec(conns[0], testspec->teardownsql);
        if (PQresultStatus(res) == PGRES_TUPLES_OK)
        {
            printResultSet(res);
        }
        else if (PQresultStatus(res) != PGRES_COMMAND_OK)
        {
            fprintf(stderr, "teardown failed: %s",
                    PQerrorMessage(conns[0]));
            /* don't exit on teardown failure */
            fflush(stderr);
        }
        PQclear(res);
    }
}

static void run_testspec ( TestSpec testspec  )  [static]

Definition at line 300 of file isolationtester.c.

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

Referenced by main().

{
    if (testspec->permutations)
        run_named_permutations(testspec);
    else
        run_all_permutations(testspec);
}

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

Definition at line 436 of file isolationtester.c.

References Step::name.

{
    char       *stepname = (char *) a;
    Step       *step = *((Step **) b);

    return strcmp(stepname, step->name);
}

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

Definition at line 427 of file isolationtester.c.

References Step::name.

Referenced by run_named_permutations().

{
    Step       *stepa = *((Step **) a);
    Step       *stepb = *((Step **) b);

    return strcmp(stepa->name, stepb->name);
}

static bool try_complete_step ( Step step,
int  flags 
) [static]

Definition at line 699 of file isolationtester.c.

References backend_pids, conn, EINTR, Step::errormsg, exit_nicely, malloc, Step::name, NULL, PG_DIAG_MESSAGE_PRIMARY, PG_DIAG_SEVERITY, PGRES_COMMAND_OK, PGRES_FATAL_ERROR, PGRES_TUPLES_OK, PQclear(), PQconsumeInput(), PQerrorMessage(), PQexecPrepared(), PQgetResult(), PQisBusy(), PQntuples(), PQresStatus(), PQresultErrorField(), PQresultErrorMessage(), PQresultStatus(), PQsocket(), PREP_WAITING, printResultSet(), select, Step::session, Step::sql, STEP_NONBLOCK, STEP_RETRY, and strerror().

Referenced by run_permutation().

{
    PGconn     *conn = conns[1 + step->session];
    fd_set      read_set;
    struct timeval timeout;
    int         sock = PQsocket(conn);
    int         ret;
    PGresult   *res;

    FD_ZERO(&read_set);

    while ((flags & STEP_NONBLOCK) && PQisBusy(conn))
    {
        FD_SET(sock, &read_set);
        timeout.tv_sec = 0;
        timeout.tv_usec = 10000;    /* Check for lock waits every 10ms. */

        ret = select(sock + 1, &read_set, NULL, NULL, &timeout);
        if (ret < 0)            /* error in select() */
        {
            if (errno == EINTR)
                continue;
            fprintf(stderr, "select failed: %s\n", strerror(errno));
            exit_nicely();
        }
        else if (ret == 0)      /* select() timeout: check for lock wait */
        {
            int         ntuples;

            res = PQexecPrepared(conns[0], PREP_WAITING, 1,
                                 &backend_pids[step->session + 1],
                                 NULL, NULL, 0);
            if (PQresultStatus(res) != PGRES_TUPLES_OK)
            {
                fprintf(stderr, "lock wait query failed: %s",
                        PQerrorMessage(conn));
                exit_nicely();
            }
            ntuples = PQntuples(res);
            PQclear(res);

            if (ntuples >= 1)   /* waiting to acquire a lock */
            {
                if (!(flags & STEP_RETRY))
                    printf("step %s: %s <waiting ...>\n",
                           step->name, step->sql);
                return true;
            }
            /* else, not waiting: give it more time */
        }
        else if (!PQconsumeInput(conn)) /* select(): data available */
        {
            fprintf(stderr, "PQconsumeInput failed: %s\n",
                    PQerrorMessage(conn));
            exit_nicely();
        }
    }

    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:
                break;
            case PGRES_TUPLES_OK:
                printResultSet(res);
                break;
            case PGRES_FATAL_ERROR:
                if (step->errormsg != NULL)
                {
                    printf("WARNING: this step had a leftover error message\n");
                    printf("%s\n", step->errormsg);
                }

                /*
                 * Detail may contain XID values, so we want to just show
                 * primary.  Beware however that libpq-generated error results
                 * may not contain subfields, only an old-style message.
                 */
                {
                    const char *sev = PQresultErrorField(res,
                                                         PG_DIAG_SEVERITY);
                    const char *msg = PQresultErrorField(res,
                                                    PG_DIAG_MESSAGE_PRIMARY);

                    if (sev && msg)
                    {
                        step->errormsg = malloc(5 + strlen(sev) + strlen(msg));
                        sprintf(step->errormsg, "%s:  %s", sev, msg);
                    }
                    else
                        step->errormsg = strdup(PQresultErrorMessage(res));
                }
                break;
            default:
                printf("unexpected result status: %s\n",
                       PQresStatus(PQresultStatus(res)));
        }
        PQclear(res);
    }

    return false;
}


Variable Documentation

const char** backend_pids = NULL [static]

Definition at line 43 of file isolationtester.c.

Referenced by main(), and try_complete_step().

PGconn** conns = NULL [static]

Definition at line 42 of file isolationtester.c.

Referenced by describeRoles().

int dry_run = false [static]

Definition at line 47 of file isolationtester.c.

Referenced by main(), and run_permutation().

int nconns = 0 [static]

Definition at line 44 of file isolationtester.c.

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

int optind

Definition at line 49 of file getopt.c.

int* piles [static]

Definition at line 293 of file isolationtester.c.

Referenced by run_all_permutations(), and run_all_permutations_recurse().