]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
sqlite: Implement transaction handling
authorTobias Brunner <tobias@strongswan.org>
Thu, 5 Sep 2013 14:50:23 +0000 (16:50 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 11 Oct 2013 13:16:05 +0000 (15:16 +0200)
src/libstrongswan/plugins/sqlite/sqlite_database.c

index 6c8b48324388c871b2af8cb16fb96fe210cbf103..b5ed7eed2807873b66a3332cf72b9a3b4f332267 100644 (file)
@@ -21,6 +21,7 @@
 #include <library.h>
 #include <utils/debug.h>
 #include <threading/mutex.h>
+#include <threading/thread_value.h>
 
 typedef struct private_sqlite_database_t private_sqlite_database_t;
 
@@ -40,11 +41,33 @@ struct private_sqlite_database_t {
        sqlite3 *db;
 
        /**
-        * mutex used to lock execute()
+        * thread-specific transaction, as transaction_t
+        */
+       thread_value_t *transaction;
+
+       /**
+        * mutex used to lock execute(), if necessary
         */
        mutex_t *mutex;
 };
 
+/**
+ * Database transaction
+ */
+typedef struct {
+
+       /**
+        * Refcounter if transaction() is called multiple times
+        */
+       refcount_t refs;
+
+       /**
+        * TRUE if transaction was rolled back
+        */
+       bool rollback;
+
+} transaction_t;
+
 /**
  * Create and run a sqlite stmt using a sql string and args
  */
@@ -284,19 +307,72 @@ METHOD(database_t, execute, int,
 METHOD(database_t, transaction, bool,
        private_sqlite_database_t *this)
 {
-       return FALSE;
+       transaction_t *trans;
+
+       trans = this->transaction->get(this->transaction);
+       if (trans)
+       {
+               ref_get(&trans->refs);
+               return TRUE;
+       }
+       if (execute(this, NULL, "BEGIN EXCLUSIVE TRANSACTION") == -1)
+       {
+               return FALSE;
+       }
+       INIT(trans,
+               .refs = 1,
+       );
+       this->transaction->set(this->transaction, trans);
+       return TRUE;
+}
+
+/**
+ * Finalize a transaction depending on the reference count and if it should be
+ * rolled back.
+ */
+static bool finalize_transaction(private_sqlite_database_t *this,
+                                                                bool rollback)
+{
+       transaction_t *trans;
+       char *command = "COMMIT TRANSACTION";
+       bool success;
+
+       trans = this->transaction->get(this->transaction);
+       if (!trans)
+       {
+               DBG1(DBG_LIB, "no database transaction found");
+               return FALSE;
+       }
+
+       if (ref_put(&trans->refs))
+       {
+               if (trans->rollback)
+               {
+                       command = "ROLLBACK TRANSACTION";
+               }
+               success = execute(this, NULL, command) != -1;
+
+               this->transaction->set(this->transaction, NULL);
+               free(trans);
+               return success;
+       }
+       else
+       {       /* set flag, can't be unset */
+               trans->rollback |= rollback;
+       }
+       return TRUE;
 }
 
 METHOD(database_t, commit, bool,
        private_sqlite_database_t *this)
 {
-       return FALSE;
+       return finalize_transaction(this, FALSE);
 }
 
 METHOD(database_t, rollback, bool,
        private_sqlite_database_t *this)
 {
-       return FALSE;
+       return finalize_transaction(this, TRUE);
 }
 
 METHOD(database_t, get_driver, db_driver_t,
@@ -323,6 +399,7 @@ METHOD(database_t, destroy, void,
        {
                DBG1(DBG_LIB, "sqlite close failed because database is busy");
        }
+       this->transaction->destroy(this->transaction);
        this->mutex->destroy(this->mutex);
        free(this);
 }
@@ -357,13 +434,14 @@ sqlite_database_t *sqlite_database_create(char *uri)
                        },
                },
                .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
+               .transaction = thread_value_create(NULL),
        );
 
        if (sqlite3_open(file, &this->db) != SQLITE_OK)
        {
                DBG1(DBG_LIB, "opening SQLite database '%s' failed: %s",
                         file, sqlite3_errmsg(this->db));
-               _destroy(this);
+               destroy(this);
                return NULL;
        }
 
@@ -371,4 +449,3 @@ sqlite_database_t *sqlite_database_create(char *uri)
 
        return &this->public;
 }
-