changeset 1938:d02c1a0d8082

Proper handling of serialization failures during SQL COMMIT
author Adam Chlipala <adam@chlipala.net>
date Mon, 23 Dec 2013 15:59:17 +0000
parents 94f9570671f0
children c52365a4ce41
files include/urweb/urweb_cpp.h src/c/cgi.c src/c/fastcgi.c src/c/http.c src/c/request.c src/c/urweb.c src/postgres.sml
diffstat 7 files changed, 57 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/include/urweb/urweb_cpp.h	Tue Dec 17 20:12:33 2013 -0500
+++ b/include/urweb/urweb_cpp.h	Mon Dec 23 15:59:17 2013 +0000
@@ -40,7 +40,8 @@
 void uw_ensure_transaction(struct uw_context *);
 failure_kind uw_begin_onError(struct uw_context *, char *msg);
 void uw_login(struct uw_context *);
-void uw_commit(struct uw_context *);
+int uw_commit(struct uw_context *);
+// ^-- returns nonzero if the transaction should be restarted
 int uw_rollback(struct uw_context *, int will_retry);
 
 __attribute__((noreturn)) void uw_error(struct uw_context *, failure_kind, const char *fmt, ...);
--- a/src/c/cgi.c	Tue Dec 17 20:12:33 2013 -0500
+++ b/src/c/cgi.c	Mon Dec 23 15:59:17 2013 +0000
@@ -134,9 +134,10 @@
 }
 
 void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) {
-  uw_ensure_transaction(ctx);
-  uw_get_app(ctx)->expunger(ctx, cli);
-  uw_commit(ctx);
+  do {
+    uw_ensure_transaction(ctx);
+    uw_get_app(ctx)->expunger(ctx, cli);
+  } while (uw_commit(ctx) && (uw_rollback(ctx, 1), 1));
 }
 
 void uw_post_expunge(uw_context ctx, void *data) {
--- a/src/c/fastcgi.c	Tue Dec 17 20:12:33 2013 -0500
+++ b/src/c/fastcgi.c	Mon Dec 23 15:59:17 2013 +0000
@@ -632,9 +632,10 @@
 }
 
 void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) {
-  uw_ensure_transaction(ctx);
-  uw_get_app(ctx)->expunger(ctx, cli);
-  uw_commit(ctx);
+  do {
+    uw_ensure_transaction(ctx);
+    uw_get_app(ctx)->expunger(ctx, cli);
+  } while (uw_commit(ctx) && (uw_rollback(ctx, 1), 1));
 }
 
 void uw_post_expunge(uw_context ctx, void *data) {
--- a/src/c/http.c	Tue Dec 17 20:12:33 2013 -0500
+++ b/src/c/http.c	Mon Dec 23 15:59:17 2013 +0000
@@ -447,9 +447,10 @@
 }
 
 void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) {
-  uw_ensure_transaction(ctx);
-  uw_get_app(ctx)->expunger(ctx, cli);
-  uw_commit(ctx);
+  do {
+    uw_ensure_transaction(ctx);
+    uw_get_app(ctx)->expunger(ctx, cli);
+  } while (uw_commit(ctx) && (uw_rollback(ctx, 1), 1));
 }
 
 void uw_post_expunge(uw_context ctx, void *data) {
--- a/src/c/request.c	Tue Dec 17 20:12:33 2013 -0500
+++ b/src/c/request.c	Mon Dec 23 15:59:17 2013 +0000
@@ -116,8 +116,10 @@
           return NULL;
     } while (r == UNLIMITED_RETRY || (r == BOUNDED_RETRY && retries_left > 0));
 
-    if (r != FATAL && r != BOUNDED_RETRY)
-      uw_commit(ctx);
+    if (r != FATAL && r != BOUNDED_RETRY) {
+      if (uw_commit(ctx))
+	r = UNLIMITED_RETRY;
+    }
 
     sleep(p->pdic.period);
   };
--- a/src/c/urweb.c	Tue Dec 17 20:12:33 2013 -0500
+++ b/src/c/urweb.c	Mon Dec 23 15:59:17 2013 +0000
@@ -3253,13 +3253,13 @@
   return s;
 }
 
-void uw_commit(uw_context ctx) {
+int uw_commit(uw_context ctx) {
   int i;
   char *sig;
 
   if (uw_has_error(ctx)) {
     uw_rollback(ctx, 0);
-    return;
+    return 0;
   }
 
   for (i = ctx->used_transactionals-1; i >= 0; --i)
@@ -3268,7 +3268,7 @@
         ctx->transactionals[i].commit(ctx->transactionals[i].data);
         if (uw_has_error(ctx)) {
           uw_rollback(ctx, 0);
-          return;
+          return 0;
         }
       }
 
@@ -3278,13 +3278,26 @@
         ctx->transactionals[i].commit(ctx->transactionals[i].data);
         if (uw_has_error(ctx)) {
           uw_rollback(ctx, 0);
-          return;
+          return 0;
         }
       }
 
-  if (ctx->transaction_started && ctx->app->db_commit(ctx)) {
-    uw_set_error_message(ctx, "Error running SQL COMMIT");
-    return;
+  if (ctx->transaction_started) {
+    int code =ctx->app->db_commit(ctx);
+
+    if (code) {
+      if (code == -1) {
+	uw_rollback(ctx, 1);
+	return 1;
+      }
+
+      for (i = ctx->used_transactionals-1; i >= 0; --i)
+	if (ctx->transactionals[i].free)
+	  ctx->transactionals[i].free(ctx->transactionals[i].data, 0);
+
+      uw_set_error_message(ctx, "Error running SQL COMMIT");
+      return 0;
+    }
   }
 
   for (i = 0; i < ctx->used_deltas; ++i) {
@@ -3390,6 +3403,8 @@
       } while (sig);
     }
   }
+
+  return 0;
 }
 
 
--- a/src/postgres.sml	Tue Dec 17 20:12:33 2013 -0500
+++ b/src/postgres.sml	Mon Dec 23 15:59:17 2013 +0000
@@ -438,7 +438,23 @@
          newline,
          newline,
          string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
-         box [string "PQclear(res);",
+         box [string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40001\")) {",
+              box [newline,
+                   string "PQclear(res);",
+                   newline,
+                   string "return -1;",
+                   newline],
+              string "}",
+              newline,
+              string "if (!strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), \"40P01\")) {",
+              box [newline,
+                   string "PQclear(res);",
+                   newline,
+                   string "return -1;",
+                   newline],
+              string "}",
+              newline,
+	      string "PQclear(res);",
               newline,
               string "return 1;",
               newline],