On Fri, Feb 11, 2011 at 02:13:22AM -0500, Noah Misch wrote: > Automated tests would go a long way toward building confidence that this patch > does the right thing. Thanks to the SSI patch, we now have an in-tree test > framework for testing interleaved transactions. The only thing it needs to be > suitable for this work is a way to handle blocked commands. If you like, I > can > try to whip something up for that. [off-list ACK followed]
Here's a patch implementing that. It applies to master, with or without your KEY LOCK patch also applied, though the expected outputs reflect the improvements from your patch. I add three isolation test specs: fk-contention: blocking-only test case from your blog post fk-deadlock: the deadlocking test case I used during patch review fk-deadlock2: Joel Jacobson's deadlocking test case When a spec permutation would have us run a command in a currently-blocked session, we cannot implement that permutation. Such permutations represent impossible real-world scenarios, anyway. For now, I just explicitly name the valid permutations in each spec file. If the test harness detects this problem, we abort the current test spec. It might be nicer to instead cancel all outstanding queries, issue rollbacks in all sessions, and continue with other permutations. I hesitated to do that, because we currently leave all transaction control in the hands of the test spec. I only support one waiting command at a time. As long as one commands continues to wait, I run other commands to completion synchronously. This decision has no impact on the current test specs, which all have two sessions. It avoided a touchy policy decision concerning deadlock detection. If two commands have blocked, it may be that a third command needs to run before they will unblock, or it may be that the two commands have formed a deadlock. We won't know for sure until deadlock_timeout elapses. If it's possible to run the next step in the permutation (i.e., it uses a different session from any blocked command), we can either do so immediately or wait out the deadlock_timeout first. The latter slows the test suite, but it makes the output more natural -- more like what one would typically after running the commands by hand. If anyone can think of a sound general policy, that would be helpful. For now, I've punted. With a default postgresql.conf, deadlock_timeout constitutes most of the run time. Reduce it to 20ms to accelerate things when running the tests repeatedly. Since timing dictates which query participating in a deadlock will be chosen for cancellation, the expected outputs bearing deadlock errors are unstable. I'm not sure how much it will come up in practice, so I have not included expected output variations to address this. I think this will work on Windows as well as pgbench does, but I haven't verified that. Sorry for the delay on this. nm
*** /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"
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers