summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore2
-rw-r--r--src/c/driver.c56
-rw-r--r--src/cjr_print.sml75
-rw-r--r--tests/aborter.sql3
-rw-r--r--tests/aborter.ur5
-rw-r--r--tests/aborter.urp4
6 files changed, 144 insertions, 1 deletions
diff --git a/.hgignore b/.hgignore
index f6368700..8c3417d4 100644
--- a/.hgignore
+++ b/.hgignore
@@ -24,3 +24,5 @@ src/config.sml
demo/out/*.html
demo/demo.*
+
+*.sql
diff --git a/src/c/driver.c b/src/c/driver.c
index 09478270..db982d96 100644
--- a/src/c/driver.c
+++ b/src/c/driver.c
@@ -51,6 +51,24 @@ static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
#define MAX_RETRIES 5
+int uw_db_begin(uw_context);
+int uw_db_commit(uw_context);
+int uw_db_rollback(uw_context);
+
+static int try_rollback(uw_context ctx) {
+ int r = uw_db_rollback(ctx);
+
+ if (r) {
+ printf("Error running SQL ROLLBACK\n");
+ uw_reset(ctx);
+ uw_write(ctx, "HTTP/1.1 500 Internal Server Error\n\r");
+ uw_write(ctx, "Content-type: text/plain\r\n\r\n");
+ uw_write(ctx, "Error running SQL ROLLBACK\n");
+ }
+
+ return r;
+}
+
static void *worker(void *data) {
int me = *(int *)data, retries_left = MAX_RETRIES;
uw_context ctx = uw_init(1024, 0);
@@ -116,6 +134,7 @@ static void *worker(void *data) {
*back = 0;
if (s = strstr(buf, "\r\n\r\n")) {
+ failure_kind fk;
char *cmd, *path, path_copy[uw_bufsize+1], *inputs;
*s = 0;
@@ -169,7 +188,20 @@ static void *worker(void *data) {
printf("Serving URI %s....\n", path);
while (1) {
- failure_kind fk;
+ if (uw_db_begin(ctx)) {
+ printf("Error running SQL BEGIN\n");
+ if (retries_left)
+ --retries_left;
+ else {
+ fk = FATAL;
+ uw_reset(ctx);
+ uw_write(ctx, "HTTP/1.1 500 Internal Server Error\n\r");
+ uw_write(ctx, "Content-type: text/plain\r\n\r\n");
+ uw_write(ctx, "Error running SQL BEGIN\n");
+
+ break;
+ }
+ }
uw_write(ctx, "HTTP/1.1 200 OK\r\n");
uw_write(ctx, "Content-type: text/html\r\n\r\n");
@@ -179,6 +211,17 @@ static void *worker(void *data) {
fk = uw_begin(ctx, path_copy);
if (fk == SUCCESS) {
uw_write(ctx, "</html>");
+
+ if (uw_db_commit(ctx)) {
+ fk = FATAL;
+
+ printf("Error running SQL COMMIT\n");
+ uw_reset(ctx);
+ uw_write(ctx, "HTTP/1.1 500 Internal Server Error\n\r");
+ uw_write(ctx, "Content-type: text/plain\r\n\r\n");
+ uw_write(ctx, "Error running SQL COMMIT\n");
+ }
+
break;
} else if (fk == BOUNDED_RETRY) {
if (retries_left) {
@@ -194,6 +237,10 @@ static void *worker(void *data) {
uw_write(ctx, "Fatal error (out of retries): ");
uw_write(ctx, uw_error_message(ctx));
uw_write(ctx, "\n");
+
+ try_rollback(ctx);
+
+ break;
}
} else if (fk == UNLIMITED_RETRY)
printf("Error triggers unlimited retry: %s\n", uw_error_message(ctx));
@@ -207,6 +254,8 @@ static void *worker(void *data) {
uw_write(ctx, uw_error_message(ctx));
uw_write(ctx, "\n");
+ try_rollback(ctx);
+
break;
} else {
printf("Unknown uw_handle return code!\n");
@@ -216,10 +265,15 @@ static void *worker(void *data) {
uw_write(ctx, "Content-type: text/plain\r\n\r\n");
uw_write(ctx, "Unknown uw_handle return code!\n");
+ try_rollback(ctx);
+
break;
}
uw_reset_keep_request(ctx);
+
+ if (try_rollback(ctx))
+ break;
}
uw_send(ctx, sock);
diff --git a/src/cjr_print.sml b/src/cjr_print.sml
index 089f98a1..7d74376e 100644
--- a/src/cjr_print.sml
+++ b/src/cjr_print.sml
@@ -1268,6 +1268,75 @@ fun p_decl env (dAll as (d, _) : decl) =
string "PQfinish(uw_get_db(ctx));",
newline,
string "}",
+ newline,
+ newline,
+
+ string "int uw_db_begin(uw_context ctx) {",
+ newline,
+ string "PGconn *conn = uw_get_db(ctx);",
+ newline,
+ string "PGresult *res = PQexec(conn, \"BEGIN\");",
+ newline,
+ newline,
+ string "if (res == NULL) return 1;",
+ newline,
+ newline,
+ string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
+ box [string "PQclear(res);",
+ newline,
+ string "return 1;",
+ newline],
+ string "}",
+ newline,
+ string "return 0;",
+ newline,
+ string "}",
+ newline,
+ newline,
+
+ string "int uw_db_commit(uw_context ctx) {",
+ newline,
+ string "PGconn *conn = uw_get_db(ctx);",
+ newline,
+ string "PGresult *res = PQexec(conn, \"COMMIT\");",
+ newline,
+ newline,
+ string "if (res == NULL) return 1;",
+ newline,
+ newline,
+ string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
+ box [string "PQclear(res);",
+ newline,
+ string "return 1;",
+ newline],
+ string "}",
+ newline,
+ string "return 0;",
+ newline,
+ string "}",
+ newline,
+ newline,
+
+ string "int uw_db_rollback(uw_context ctx) {",
+ newline,
+ string "PGconn *conn = uw_get_db(ctx);",
+ newline,
+ string "PGresult *res = PQexec(conn, \"ROLLBACK\");",
+ newline,
+ newline,
+ string "if (res == NULL) return 1;",
+ newline,
+ newline,
+ string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
+ box [string "PQclear(res);",
+ newline,
+ string "return 1;",
+ newline],
+ string "}",
+ newline,
+ string "return 0;",
+ newline,
+ string "}",
newline]
| DPreparedStatements ss =>
@@ -2158,6 +2227,12 @@ fun p_file env (ds, ps) =
else
box [newline,
string "void uw_db_init(uw_context ctx) { };",
+ newline,
+ string "int uw_db_begin(uw_context ctx) { return 0; };",
+ newline,
+ string "int uw_db_commit(uw_context ctx) { return 0; };",
+ newline,
+ string "int uw_db_rollback(uw_context ctx) { return 0; };",
newline]]
end
diff --git a/tests/aborter.sql b/tests/aborter.sql
new file mode 100644
index 00000000..ab6110fc
--- /dev/null
+++ b/tests/aborter.sql
@@ -0,0 +1,3 @@
+CREATE TABLE uw_Aborter_t(uw_a int8 NOT NULL);
+
+ \ No newline at end of file
diff --git a/tests/aborter.ur b/tests/aborter.ur
new file mode 100644
index 00000000..0921bdfc
--- /dev/null
+++ b/tests/aborter.ur
@@ -0,0 +1,5 @@
+table t : {A : int}
+
+fun main () : transaction page =
+ () <- dml (INSERT INTO t (A) VALUES (0));
+ return (error <xml>No way, Jose!</xml>)
diff --git a/tests/aborter.urp b/tests/aborter.urp
new file mode 100644
index 00000000..fc1925ae
--- /dev/null
+++ b/tests/aborter.urp
@@ -0,0 +1,4 @@
+database dbname=aborter
+sql aborter.sql
+
+aborter