diff options
Diffstat (limited to 'src/libstrongswan/plugins/sqlite/sqlite_database.c')
-rw-r--r-- | src/libstrongswan/plugins/sqlite/sqlite_database.c | 107 |
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; } - |