summaryrefslogtreecommitdiff
path: root/src/libstrongswan/plugins/sqlite/sqlite_database.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/plugins/sqlite/sqlite_database.c')
-rw-r--r--src/libstrongswan/plugins/sqlite/sqlite_database.c107
1 files changed, 104 insertions, 3 deletions
diff --git a/src/libstrongswan/plugins/sqlite/sqlite_database.c b/src/libstrongswan/plugins/sqlite/sqlite_database.c
index 41d45dee7..7b4767855 100644
--- a/src/libstrongswan/plugins/sqlite/sqlite_database.c
+++ b/src/libstrongswan/plugins/sqlite/sqlite_database.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2013 Tobias Brunner
* Copyright (C) 2007 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -20,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;
@@ -39,12 +41,34 @@ 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
*/
static sqlite3_stmt* run(private_sqlite_database_t *this, char *sql,
@@ -280,6 +304,79 @@ METHOD(database_t, execute, int,
return affected;
}
+METHOD(database_t, transaction, bool,
+ private_sqlite_database_t *this, bool serializable)
+{
+ transaction_t *trans;
+ char *cmd = serializable ? "BEGIN EXCLUSIVE TRANSACTION"
+ : "BEGIN TRANSACTION";
+
+ trans = this->transaction->get(this->transaction);
+ if (trans)
+ {
+ ref_get(&trans->refs);
+ return TRUE;
+ }
+ if (execute(this, NULL, cmd) == -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 finalize_transaction(this, FALSE);
+}
+
+METHOD(database_t, rollback, bool,
+ private_sqlite_database_t *this)
+{
+ return finalize_transaction(this, TRUE);
+}
+
METHOD(database_t, get_driver, db_driver_t,
private_sqlite_database_t *this)
{
@@ -304,6 +401,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);
}
@@ -330,18 +428,22 @@ sqlite_database_t *sqlite_database_create(char *uri)
.db = {
.query = _query,
.execute = _execute,
+ .transaction = _transaction,
+ .commit = _commit,
+ .rollback = _rollback,
.get_driver = _get_driver,
.destroy = _destroy,
},
},
.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;
}
@@ -349,4 +451,3 @@ sqlite_database_t *sqlite_database_create(char *uri)
return &this->public;
}
-