diff options
author | Tobias Brunner <tobias@strongswan.org> | 2013-09-05 16:46:24 +0200 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2013-10-11 15:16:04 +0200 |
commit | f3cb889c9b4539ef0669fd080122fb129a5eea67 (patch) | |
tree | 1055085e33694d28e27adb9b4131c886500ccab4 | |
parent | 947b76cda8b28f8b07e30977ca1c902ed8736dbe (diff) | |
download | strongswan-f3cb889c9b4539ef0669fd080122fb129a5eea67.tar.bz2 strongswan-f3cb889c9b4539ef0669fd080122fb129a5eea67.tar.xz |
mysql: Implement transaction handling
-rw-r--r-- | src/libstrongswan/plugins/mysql/mysql_database.c | 126 |
1 files changed, 119 insertions, 7 deletions
diff --git a/src/libstrongswan/plugins/mysql/mysql_database.c b/src/libstrongswan/plugins/mysql/mysql_database.c index 0757c2a32..7a612ff8b 100644 --- a/src/libstrongswan/plugins/mysql/mysql_database.c +++ b/src/libstrongswan/plugins/mysql/mysql_database.c @@ -51,6 +51,11 @@ struct private_mysql_database_t { linked_list_t *pool; /** + * thread-specific transaction, as transaction_t + */ + thread_value_t *transaction; + + /** * mutex to lock pool */ mutex_t *mutex; @@ -100,6 +105,28 @@ struct conn_t { }; /** + * database transaction + */ +typedef struct { + + /** + * Reference to the specific connection we started the transaction on + */ + conn_t *conn; + + /** + * Refcounter if transaction() is called multiple times + */ + refcount_t refs; + + /** + * TRUE if transaction was rolled back + */ + bool rollback; + +} transaction_t; + +/** * Release a mysql connection */ static void conn_release(private_mysql_database_t *this, conn_t *conn) @@ -110,6 +137,16 @@ static void conn_release(private_mysql_database_t *this, conn_t *conn) } /** + * Destroy a transaction and release the connection + */ +static void transaction_destroy(private_mysql_database_t *this, + transaction_t *trans) +{ + conn_release(this, trans->conn); + free(trans); +} + +/** * thread specific initialization flag */ thread_value_t *initialized; @@ -161,13 +198,24 @@ static void conn_destroy(conn_t *this) /** * Acquire/Reuse a mysql connection */ -static conn_t *conn_get(private_mysql_database_t *this) +static conn_t *conn_get(private_mysql_database_t *this, transaction_t **trans) { conn_t *current, *found = NULL; enumerator_t *enumerator; + transaction_t *transaction; thread_initialize(); + transaction = this->transaction->get(this->transaction); + if (transaction) + { + if (trans) + { + *trans = transaction; + } + return transaction->conn; + } + while (TRUE) { this->mutex->lock(this->mutex); @@ -490,7 +538,7 @@ METHOD(database_t, query, enumerator_t*, mysql_enumerator_t *enumerator = NULL; conn_t *conn; - conn = conn_get(this); + conn = conn_get(this, NULL); if (!conn) { return NULL; @@ -582,7 +630,7 @@ METHOD(database_t, execute, int, conn_t *conn; int affected = -1; - conn = conn_get(this); + conn = conn_get(this, NULL); if (!conn) { return -1; @@ -606,19 +654,81 @@ METHOD(database_t, execute, int, METHOD(database_t, transaction, bool, private_mysql_database_t *this) { - return FALSE; + transaction_t *trans = NULL; + conn_t *conn; + + conn = conn_get(this, &trans); + if (!conn) + { + return FALSE; + } + else if (trans) + { + ref_get(&trans->refs); + return TRUE; + } + /* these statements are not supported in prepared statements that are used + * by the execute() method */ + if (mysql_query(conn->mysql, "START TRANSACTION") != 0) + { + DBG1(DBG_LIB, "starting transaction failed: %s", + mysql_error(conn->mysql)); + conn_release(this, conn); + return FALSE; + } + INIT(trans, + .conn = conn, + .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_mysql_database_t *this, + bool rollback) +{ + transaction_t *trans; + char *command = "COMMIT"; + bool success; + + trans = this->transaction->get(this->transaction); + if (!trans) + { + DBG1(DBG_LIB, "no database transaction found"); + return FALSE; + } + /* set flag, can't be unset */ + trans->rollback |= rollback; + + if (ref_put(&trans->refs)) + { + if (trans->rollback) + { + command = "ROLLBACK"; + } + success = mysql_query(trans->conn->mysql, command) == 0; + + this->transaction->set(this->transaction, NULL); + transaction_destroy(this, trans); + return success; + } + return TRUE; } METHOD(database_t, commit, bool, private_mysql_database_t *this) { - return FALSE; + return finalize_transaction(this, FALSE); } METHOD(database_t, rollback, bool, private_mysql_database_t *this) { - return FALSE; + return finalize_transaction(this, TRUE); } METHOD(database_t, get_driver,db_driver_t, @@ -630,6 +740,7 @@ METHOD(database_t, get_driver,db_driver_t, METHOD(database_t, destroy, void, private_mysql_database_t *this) { + this->transaction->destroy(this->transaction); this->pool->destroy_function(this->pool, (void*)conn_destroy); this->mutex->destroy(this->mutex); free(this->host); @@ -721,9 +832,10 @@ mysql_database_t *mysql_database_create(char *uri) } this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); this->pool = linked_list_create(); + this->transaction = thread_value_create(NULL); /* check connectivity */ - conn = conn_get(this); + conn = conn_get(this, NULL); if (!conn) { destroy(this); |