fklocks-tests-v1.patch
text/plain
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
Series: patch v1
| File | + | − |
|---|---|---|
| src/test/isolation/expected/fk-contention.out | 16 | 0 |
| src/test/isolation/expected/fk-deadlock2.out | 106 | 0 |
| src/test/isolation/expected/fk-deadlock.out | 63 | 0 |
| src/test/isolation/isolation_schedule | 3 | 0 |
| src/test/isolation/isolationtester.c | 180 | 1 |
| src/test/isolation/specs/fk-contention.spec | 19 | 0 |
| src/test/isolation/specs/fk-deadlock2.spec | 59 | 0 |
| src/test/isolation/specs/fk-deadlock.spec | 54 | 0 |
*** /dev/null
--- b/src/test/isolation/expected/fk-contention.out
***************
*** 0 ****
--- 1,16 ----
+ Parsed test spec with 2 sessions
+
+ starting permutation: ins com upd
+ step ins: INSERT INTO bar VALUES (42);
+ step com: COMMIT;
+ step upd: UPDATE foo SET b = 'Hello World';
+
+ starting permutation: ins upd com
+ step ins: INSERT INTO bar VALUES (42);
+ step upd: UPDATE foo SET b = 'Hello World';
+ step com: COMMIT;
+
+ starting permutation: upd ins com
+ step upd: UPDATE foo SET b = 'Hello World';
+ step ins: INSERT INTO bar VALUES (42);
+ step com: COMMIT;
*** /dev/null
--- b/src/test/isolation/expected/fk-deadlock.out
***************
*** 0 ****
--- 1,63 ----
+ Parsed test spec with 2 sessions
+
+ starting permutation: s1i s1u s1c s2i s2u s2c
+ step s1i: INSERT INTO child VALUES (1, 1);
+ step s1u: UPDATE parent SET aux = 'bar';
+ step s1c: COMMIT;
+ step s2i: INSERT INTO child VALUES (2, 1);
+ step s2u: UPDATE parent SET aux = 'baz';
+ step s2c: COMMIT;
+
+ starting permutation: s1i s1u s2i s1c s2u s2c
+ step s1i: INSERT INTO child VALUES (1, 1);
+ step s1u: UPDATE parent SET aux = 'bar';
+ step s2i: INSERT INTO child VALUES (2, 1); <waiting ...>
+ step s1c: COMMIT;
+ step s2i: <... completed>
+ step s2u: UPDATE parent SET aux = 'baz';
+ step s2c: COMMIT;
+
+ starting permutation: s1i s2i s1u s2u s1c s2c
+ step s1i: INSERT INTO child VALUES (1, 1);
+ step s2i: INSERT INTO child VALUES (2, 1);
+ step s1u: UPDATE parent SET aux = 'bar';
+ step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
+ step s1c: COMMIT;
+ step s2u: <... completed>
+ step s2c: COMMIT;
+
+ starting permutation: s1i s2i s2u s1u s2c s1c
+ step s1i: INSERT INTO child VALUES (1, 1);
+ step s2i: INSERT INTO child VALUES (2, 1);
+ step s2u: UPDATE parent SET aux = 'baz';
+ step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
+ step s2c: COMMIT;
+ step s1u: <... completed>
+ step s1c: COMMIT;
+
+ starting permutation: s2i s1i s1u s2u s1c s2c
+ step s2i: INSERT INTO child VALUES (2, 1);
+ step s1i: INSERT INTO child VALUES (1, 1);
+ step s1u: UPDATE parent SET aux = 'bar';
+ step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
+ step s1c: COMMIT;
+ step s2u: <... completed>
+ step s2c: COMMIT;
+
+ starting permutation: s2i s1i s2u s1u s2c s1c
+ step s2i: INSERT INTO child VALUES (2, 1);
+ step s1i: INSERT INTO child VALUES (1, 1);
+ step s2u: UPDATE parent SET aux = 'baz';
+ step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
+ step s2c: COMMIT;
+ step s1u: <... completed>
+ step s1c: COMMIT;
+
+ starting permutation: s2i s2u s1i s2c s1u s1c
+ step s2i: INSERT INTO child VALUES (2, 1);
+ step s2u: UPDATE parent SET aux = 'baz';
+ step s1i: INSERT INTO child VALUES (1, 1); <waiting ...>
+ step s2c: COMMIT;
+ step s1i: <... completed>
+ step s1u: UPDATE parent SET aux = 'bar';
+ step s1c: COMMIT;
*** /dev/null
--- b/src/test/isolation/expected/fk-deadlock2.out
***************
*** 0 ****
--- 1,106 ----
+ Parsed test spec with 2 sessions
+
+ starting permutation: s1u1 s1u2 s1c s2u1 s2u2 s2c
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1c: COMMIT;
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2c: COMMIT;
+
+ starting permutation: s1u1 s1u2 s2u1 s1c s2u2 s2c
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+ step s1c: COMMIT;
+ step s2u1: <... completed>
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2c: COMMIT;
+
+ starting permutation: s1u1 s2u1 s1u2 s2u2 s1c s2c
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u2: <... completed>
+ ERROR: deadlock detected
+ step s1c: COMMIT;
+ step s2c: COMMIT;
+
+ starting permutation: s1u1 s2u1 s1u2 s2u2 s2c s1c
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u2: <... completed>
+ ERROR: deadlock detected
+ step s2c: COMMIT;
+ step s1c: COMMIT;
+
+ starting permutation: s1u1 s2u1 s2u2 s1u2 s1c s2c
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2u2: <... completed>
+ ERROR: deadlock detected
+ step s1c: COMMIT;
+ step s2c: COMMIT;
+
+ starting permutation: s1u1 s2u1 s2u2 s1u2 s2c s1c
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2u2: <... completed>
+ ERROR: deadlock detected
+ step s2c: COMMIT;
+ step s1c: COMMIT;
+
+ starting permutation: s2u1 s1u1 s1u2 s2u2 s1c s2c
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u2: <... completed>
+ ERROR: deadlock detected
+ step s1c: COMMIT;
+ step s2c: COMMIT;
+
+ starting permutation: s2u1 s1u1 s1u2 s2u2 s2c s1c
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u2: <... completed>
+ ERROR: deadlock detected
+ step s2c: COMMIT;
+ step s1c: COMMIT;
+
+ starting permutation: s2u1 s1u1 s2u2 s1u2 s1c s2c
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2u2: <... completed>
+ ERROR: deadlock detected
+ step s1c: COMMIT;
+ step s2c: COMMIT;
+
+ starting permutation: s2u1 s1u1 s2u2 s1u2 s2c s1c
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2u2: <... completed>
+ ERROR: deadlock detected
+ step s2c: COMMIT;
+ step s1c: COMMIT;
+
+ starting permutation: s2u1 s2u2 s1u1 s2c s1u2 s1c
+ step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+ step s2c: COMMIT;
+ step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ step s1c: COMMIT;
*** a/src/test/isolation/isolation_schedule
--- b/src/test/isolation/isolation_schedule
***************
*** 9,11 **** test: ri-trigger
--- 9,14 ----
test: partial-index
test: two-ids
test: multiple-row-versions
+ test: fk-contention
+ test: fk-deadlock
+ test: fk-deadlock2
*** a/src/test/isolation/isolationtester.c
--- b/src/test/isolation/isolationtester.c
***************
*** 9,23 ****
#include <windows.h>
#endif
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
- #include "libpq-fe.h"
#include "isolationtester.h"
static PGconn **conns = NULL;
static int nconns = 0;
static void run_all_permutations(TestSpec *testspec);
--- 9,36 ----
#include <windows.h>
#endif
+ #include <errno.h>
+ #include <unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+ #ifdef HAVE_SYS_SELECT_H
+ #include <sys/select.h>
+ #endif
+
+ #include "libpq-fe.h"
#include "isolationtester.h"
+ #define PREP_WAITING "isolationtester_waiting"
+
+ /*
+ * conns[0] is the global setup, teardown, and watchdog connection. Additional
+ * connections represent spec-defined sessions.
+ */
static PGconn **conns = NULL;
+ static const char **backend_ids = NULL;
static int nconns = 0;
static void run_all_permutations(TestSpec *testspec);
***************
*** 25,30 **** static void run_all_permutations_recurse(TestSpec *testspec, int nsteps, Step **
--- 38,47 ----
static void run_named_permutations(TestSpec *testspec);
static void run_permutation(TestSpec *testspec, int nsteps, Step **steps);
+ #define STEP_NONBLOCK 0x1 /* return 0 as soon as cmd waits for a lock */
+ #define STEP_RETRY 0x2 /* this is a retry of a previously-waiting cmd */
+ static int 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);
***************
*** 46,51 **** main(int argc, char **argv)
--- 63,69 ----
const char *conninfo;
TestSpec *testspec;
int i;
+ PGresult *res;
/*
* If the user supplies a parameter on the command line, use it as the
***************
*** 63,75 **** main(int argc, char **argv)
testspec = &parseresult;
printf("Parsed test spec with %d sessions\n", testspec->nsessions);
! /* Establish connections to the database, one for each session */
! nconns = testspec->nsessions;
conns = calloc(nconns, sizeof(PGconn *));
! for (i = 0; i < testspec->nsessions; i++)
{
- PGresult *res;
-
conns[i] = PQconnectdb(conninfo);
if (PQstatus(conns[i]) != CONNECTION_OK)
{
--- 81,95 ----
testspec = &parseresult;
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_ids = calloc(nconns, sizeof(*backend_ids));
! for (i = 0; i < nconns; i++)
{
conns[i] = PQconnectdb(conninfo);
if (PQstatus(conns[i]) != CONNECTION_OK)
{
***************
*** 89,94 **** main(int argc, char **argv)
--- 109,136 ----
exit_nicely();
}
PQclear(res);
+
+ /* Get the backend ID for lock wait checking. */
+ res = PQexec(conns[i], "SELECT i FROM pg_stat_get_backend_idset() t(i) "
+ "WHERE pg_stat_get_backend_pid(i) = pg_backend_pid()");
+ if (PQresultStatus(res) == PGRES_TUPLES_OK)
+ {
+ if (PQntuples(res) == 1 && PQnfields(res) == 1)
+ backend_ids[i] = strdup(PQgetvalue(res, 0, 0));
+ else
+ {
+ fprintf(stderr, "backend id query returned %d rows and %d columns, expected 1 row and 1 column",
+ PQntuples(res), PQnfields(res));
+ exit_nicely();
+ }
+ }
+ else
+ {
+ fprintf(stderr, "backend id query failed: %s",
+ PQerrorMessage(conns[i]));
+ exit_nicely();
+ }
+ PQclear(res);
}
/* Set the session index fields in steps. */
***************
*** 100,105 **** main(int argc, char **argv)
--- 142,157 ----
session->steps[stepindex]->session = i;
}
+ res = PQprepare(conns[0], PREP_WAITING,
+ "SELECT 1 WHERE pg_stat_get_backend_waiting($1)", 0, NULL);
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ fprintf(stderr, "prepare of lock wait query failed: %s",
+ PQerrorMessage(conns[0]));
+ exit_nicely();
+ }
+ PQclear(res);
+
/*
* Run the permutations specified in the spec, or all if none were
* explicitly specified.
***************
*** 254,259 **** run_permutation(TestSpec *testspec, int nsteps, Step **steps)
--- 306,312 ----
{
PGresult *res;
int i;
+ Step *waiting = NULL;
printf("\nstarting permutation:");
for (i = 0; i < nsteps; i++)
***************
*** 277,288 **** run_permutation(TestSpec *testspec, int nsteps, Step **steps)
{
if (testspec->sessions[i]->setupsql)
{
! res = PQexec(conns[i], testspec->sessions[i]->setupsql);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "setup of session %s failed: %s",
testspec->sessions[i]->name,
! PQerrorMessage(conns[0]));
exit_nicely();
}
PQclear(res);
--- 330,341 ----
{
if (testspec->sessions[i]->setupsql)
{
! res = PQexec(conns[i+1], testspec->sessions[i]->setupsql);
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);
***************
*** 293,334 **** run_permutation(TestSpec *testspec, int nsteps, Step **steps)
for (i = 0; i < nsteps; i++)
{
Step *step = steps[i];
- printf("step %s: %s\n", step->name, step->sql);
- res = PQexec(conns[step->session], step->sql);
! switch(PQresultStatus(res))
{
! case PGRES_COMMAND_OK:
! break;
!
! case PGRES_TUPLES_OK:
! printResultSet(res);
! break;
! case PGRES_FATAL_ERROR:
! /* Detail may contain xid values, so just show primary. */
! printf("%s: %s\n", PQresultErrorField(res, PG_DIAG_SEVERITY),
! PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY));
! break;
! default:
! printf("unexpected result status: %s\n",
! PQresStatus(PQresultStatus(res)));
}
! PQclear(res);
}
/* Perform per-session teardown */
for (i = 0; i < testspec->nsessions; i++)
{
if (testspec->sessions[i]->teardownsql)
{
! res = PQexec(conns[i], testspec->sessions[i]->teardownsql);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "teardown of session %s failed: %s",
testspec->sessions[i]->name,
! PQerrorMessage(conns[0]));
/* don't exit on teardown failure */
}
PQclear(res);
--- 346,387 ----
for (i = 0; i < nsteps; i++)
{
Step *step = steps[i];
! if (!PQsendQuery(conns[1 + step->session], step->sql))
{
! fprintf(stdout, "failed to send query: %s\n",
! 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. */
! if (try_complete_step(waiting, STEP_NONBLOCK | STEP_RETRY))
! waiting = NULL;
}
! else if (!try_complete_step(step, STEP_NONBLOCK))
! waiting = step;
}
+ /* Finish any waiting query. */
+ if (waiting != NULL)
+ try_complete_step(waiting, STEP_RETRY);
+
/* 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 */
}
PQclear(res);
***************
*** 350,355 **** run_permutation(TestSpec *testspec, int nsteps, Step **steps)
--- 403,507 ----
}
}
+ /*
+ * Our caller already sent the query associated with this step. Wait for it to
+ * either complete or (only when given the STEP_NONBLOCK flag) to block while
+ * waiting for a lock. We assume that any lock wait will persist until we have
+ * executed additional steps in the permutation. This is not fully robust -- a
+ * concurrent autovacuum could briefly take a lock with which we conflict. The
+ * risk may be low enough to discount.
+ *
+ * When calling this function on behalf of a given step for a second or later
+ * time, pass the STEP_RETRY flag. This only affects the messages printed.
+ *
+ * If the STEP_NONBLOCK flag was specified and the query is waiting to acquire a
+ * lock, returns 0. Otherwise, returns 1.
+ */
+ static int
+ try_complete_step(Step *step, int flags)
+ {
+ 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() */
+ {
+ 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_ids[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 0;
+ }
+ /* else, not waiting: give it more time */
+ }
+ else if (!PQconsumeInput(conn)) /* select(): data available */
+ {
+ fprintf(stderr, "PQconsumeInput failed: %s", 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:
+ /* Detail may contain xid values, so just show primary. */
+ printf("%s: %s\n", PQresultErrorField(res, PG_DIAG_SEVERITY),
+ PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY));
+ break;
+ default:
+ printf("unexpected result status: %s\n",
+ PQresStatus(PQresultStatus(res)));
+ }
+ PQclear(res);
+ }
+
+ return 1;
+ }
+
static void
printResultSet(PGresult *res)
{
*** /dev/null
--- b/src/test/isolation/specs/fk-contention.spec
***************
*** 0 ****
--- 1,19 ----
+ setup
+ {
+ CREATE TABLE foo (a int PRIMARY KEY, b text);
+ CREATE TABLE bar (a int NOT NULL REFERENCES foo);
+ INSERT INTO foo VALUES (42);
+ }
+
+ teardown
+ {
+ DROP TABLE foo, bar;
+ }
+
+ session "s1"
+ setup { BEGIN; }
+ step "ins" { INSERT INTO bar VALUES (42); }
+ step "com" { COMMIT; }
+
+ session "s2"
+ step "upd" { UPDATE foo SET b = 'Hello World'; }
*** /dev/null
--- b/src/test/isolation/specs/fk-deadlock.spec
***************
*** 0 ****
--- 1,54 ----
+ setup
+ {
+ CREATE TABLE parent (
+ parent_key int PRIMARY KEY,
+ aux text NOT NULL
+ );
+
+ CREATE TABLE child (
+ child_key int PRIMARY KEY,
+ parent_key int NOT NULL REFERENCES parent
+ );
+
+ INSERT INTO parent VALUES (1, 'foo');
+ }
+
+ teardown
+ {
+ DROP TABLE parent, child;
+ }
+
+ session "s1"
+ setup { BEGIN; }
+ step "s1i" { INSERT INTO child VALUES (1, 1); }
+ step "s1u" { UPDATE parent SET aux = 'bar'; }
+ step "s1c" { COMMIT; }
+
+ session "s2"
+ setup { BEGIN; }
+ step "s2i" { INSERT INTO child VALUES (2, 1); }
+ step "s2u" { UPDATE parent SET aux = 'baz'; }
+ step "s2c" { COMMIT; }
+
+ ## Most theoretical permutations require that a blocked session execute a
+ ## command, making them impossible in practice.
+ permutation "s1i" "s1u" "s1c" "s2i" "s2u" "s2c"
+ permutation "s1i" "s1u" "s2i" "s1c" "s2u" "s2c"
+ #permutation "s1i" "s1u" "s2i" "s2u" "s1c" "s2c"
+ #permutation "s1i" "s1u" "s2i" "s2u" "s2c" "s1c"
+ #permutation "s1i" "s2i" "s1u" "s1c" "s2u" "s2c"
+ permutation "s1i" "s2i" "s1u" "s2u" "s1c" "s2c"
+ #permutation "s1i" "s2i" "s1u" "s2u" "s2c" "s1c"
+ #permutation "s1i" "s2i" "s2u" "s1u" "s1c" "s2c"
+ permutation "s1i" "s2i" "s2u" "s1u" "s2c" "s1c"
+ #permutation "s1i" "s2i" "s2u" "s2c" "s1u" "s1c"
+ #permutation "s2i" "s1i" "s1u" "s1c" "s2u" "s2c"
+ permutation "s2i" "s1i" "s1u" "s2u" "s1c" "s2c"
+ #permutation "s2i" "s1i" "s1u" "s2u" "s2c" "s1c"
+ #permutation "s2i" "s1i" "s2u" "s1u" "s1c" "s2c"
+ permutation "s2i" "s1i" "s2u" "s1u" "s2c" "s1c"
+ #permutation "s2i" "s1i" "s2u" "s2c" "s1u" "s1c"
+ #permutation "s2i" "s2u" "s1i" "s1u" "s1c" "s2c"
+ #permutation "s2i" "s2u" "s1i" "s1u" "s2c" "s1c"
+ permutation "s2i" "s2u" "s1i" "s2c" "s1u" "s1c"
+ #permutation "s2i" "s2u" "s2c" "s1i" "s1u" "s1c"
*** /dev/null
--- b/src/test/isolation/specs/fk-deadlock2.spec
***************
*** 0 ****
--- 1,59 ----
+ setup
+ {
+ CREATE TABLE A (
+ AID integer not null,
+ Col1 integer,
+ PRIMARY KEY (AID)
+ );
+
+ CREATE TABLE B (
+ BID integer not null,
+ AID integer not null,
+ Col2 integer,
+ PRIMARY KEY (BID),
+ FOREIGN KEY (AID) REFERENCES A(AID)
+ );
+
+ INSERT INTO A (AID) VALUES (1);
+ INSERT INTO B (BID,AID) VALUES (2,1);
+ }
+
+ teardown
+ {
+ DROP TABLE a, b;
+ }
+
+ session "s1"
+ setup { BEGIN; }
+ step "s1u1" { UPDATE A SET Col1 = 1 WHERE AID = 1; }
+ step "s1u2" { UPDATE B SET Col2 = 1 WHERE BID = 2; }
+ step "s1c" { COMMIT; }
+
+ session "s2"
+ setup { BEGIN; }
+ step "s2u1" { UPDATE B SET Col2 = 1 WHERE BID = 2; }
+ step "s2u2" { UPDATE B SET Col2 = 1 WHERE BID = 2; }
+ step "s2c" { COMMIT; }
+
+ ## Many theoretical permutations require that a blocked session execute a
+ ## command, making them impossible in practice.
+ permutation "s1u1" "s1u2" "s1c" "s2u1" "s2u2" "s2c"
+ permutation "s1u1" "s1u2" "s2u1" "s1c" "s2u2" "s2c"
+ #permutation "s1u1" "s1u2" "s2u1" "s2u2" "s1c" "s2c"
+ #permutation "s1u1" "s1u2" "s2u1" "s2u2" "s2c" "s1c"
+ #permutation "s1u1" "s2u1" "s1u2" "s1c" "s2u2" "s2c"
+ permutation "s1u1" "s2u1" "s1u2" "s2u2" "s1c" "s2c"
+ permutation "s1u1" "s2u1" "s1u2" "s2u2" "s2c" "s1c"
+ permutation "s1u1" "s2u1" "s2u2" "s1u2" "s1c" "s2c"
+ permutation "s1u1" "s2u1" "s2u2" "s1u2" "s2c" "s1c"
+ #permutation "s1u1" "s2u1" "s2u2" "s2c" "s1u2" "s1c"
+ #permutation "s2u1" "s1u1" "s1u2" "s1c" "s2u2" "s2c"
+ permutation "s2u1" "s1u1" "s1u2" "s2u2" "s1c" "s2c"
+ permutation "s2u1" "s1u1" "s1u2" "s2u2" "s2c" "s1c"
+ permutation "s2u1" "s1u1" "s2u2" "s1u2" "s1c" "s2c"
+ permutation "s2u1" "s1u1" "s2u2" "s1u2" "s2c" "s1c"
+ #permutation "s2u1" "s1u1" "s2u2" "s2c" "s1u2" "s1c"
+ #permutation "s2u1" "s2u2" "s1u1" "s1u2" "s1c" "s2c"
+ #permutation "s2u1" "s2u2" "s1u1" "s1u2" "s2c" "s1c"
+ permutation "s2u1" "s2u2" "s1u1" "s2c" "s1u2" "s1c"
+ #permutation "s2u1" "s2u2" "s2c" "s1u1" "s1u2" "s1c"