g10: Support nested transactions on the TOFU DB.
authorNeal H. Walfield <neal@g10code.com>
Mon, 29 Aug 2016 13:13:45 +0000 (15:13 +0200)
committerNeal H. Walfield <neal@g10code.com>
Tue, 30 Aug 2016 13:54:41 +0000 (15:54 +0200)
* g10/gpg.h (struct server_control_s): New field in_transaction.
* g10/tofu.c (struct tofu_dbs_s): Remove fields savepoint_inner and
savepoint_inner_commit.
(begin_transaction): Increment CTRL->TOFU.IN_TRANSACTION.  Name the
savepoint according to the nesting level.
(end_transaction): Name the savepoint according to the nesting level.
Decrement CTRL->TOFU.IN_TRANSACTION.
(rollback_transaction): Likewise.  Only ever rollback a non-batch
transaction.
(opendbs): Assert that there are no outstanding transactions.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
g10/gpg.h
g10/tofu.c

index 1aaff2f..154da0d 100644 (file)
--- a/g10/gpg.h
+++ b/g10/gpg.h
@@ -82,6 +82,7 @@ struct server_control_s
   /* Local data for tofu.c  */
   struct {
     tofu_dbs_t dbs;
+    int    in_transaction;
     int    batch_update_ref;
     time_t batch_update_started;
   } tofu;
index e15b564..809dac9 100644 (file)
@@ -70,9 +70,6 @@ struct tofu_dbs_s
     sqlite3_stmt *savepoint_batch;
     sqlite3_stmt *savepoint_batch_commit;
 
-    sqlite3_stmt *savepoint_inner;
-    sqlite3_stmt *savepoint_inner_commit;
-
     sqlite3_stmt *record_binding_get_old_policy;
     sqlite3_stmt *record_binding_update;
     sqlite3_stmt *record_binding_update2;
@@ -209,9 +206,12 @@ begin_transaction (ctrl_t ctrl, int only_batch)
   if (only_batch)
     return 0;
 
-  rc = gpgsql_stepx (dbs->db, &dbs->s.savepoint_inner,
-                      NULL, NULL, &err,
-                      "savepoint inner;", SQLITE_ARG_END);
+  log_assert(ctrl->tofu.in_transaction >= 0);
+  ctrl->tofu.in_transaction ++;
+
+  rc = gpgsql_exec_printf (dbs->db, NULL, NULL, &err,
+                           "savepoint inner%d;",
+                           ctrl->tofu.in_transaction);
   if (rc)
     {
       log_error (_("error beginning transaction on TOFU database: %s\n"),
@@ -263,9 +263,9 @@ end_transaction (ctrl_t ctrl, int only_batch)
   if (only_batch)
     return 0;
 
-  rc = gpgsql_stepx (dbs->db, &dbs->s.savepoint_inner_commit,
-                      NULL, NULL, &err,
-                      "release inner;", SQLITE_ARG_END);
+  log_assert (ctrl->tofu.in_transaction > 0);
+  rc = gpgsql_exec_printf (dbs->db, NULL, NULL, &err,
+                           "release inner%d;", ctrl->tofu.in_transaction);
   if (rc)
     {
       log_error (_("error committing transaction on TOFU database: %s\n"),
@@ -274,6 +274,8 @@ end_transaction (ctrl_t ctrl, int only_batch)
       return gpg_error (GPG_ERR_GENERAL);
     }
 
+  ctrl->tofu.in_transaction --;
+
   return 0;
 }
 
@@ -287,18 +289,15 @@ rollback_transaction (ctrl_t ctrl)
 
   if (!dbs)
     return 0;  /* Shortcut to allow for easier cleanup code.  */
+  log_assert (ctrl->tofu.in_transaction > 0);
 
-  if (dbs->batch_update)
-    {
-      /* Just undo the most recent update; don't revert any progress
-         made by the batch transaction.  */
-      rc = sqlite3_exec (dbs->db, "rollback to inner;", NULL, NULL, &err);
-    }
-  else
-    {
-      /* Rollback the whole she-bang.  */
-      rc = sqlite3_exec (dbs->db, "rollback;", NULL, NULL, &err);
-    }
+  /* Be careful to not any progress made by closed transactions in
+     batch mode.  */
+  rc = gpgsql_exec_printf (dbs->db, NULL, NULL, &err,
+                           "rollback to inner%d;",
+                           ctrl->tofu.in_transaction);
+
+  ctrl->tofu.in_transaction --;
 
   if (rc)
     {
@@ -694,6 +693,8 @@ tofu_closedbs (ctrl_t ctrl)
   tofu_dbs_t dbs;
   sqlite3_stmt **statements;
 
+  log_assert(ctrl->tofu.in_transaction == 0);
+
   dbs = ctrl->tofu.dbs;
   if (!dbs)
     return;  /* Not initialized.  */