linked_list_t *pool;
/**
+ * thread-specific transaction, as transaction_t
+ */
+ thread_value_t *transaction;
+
+ /**
* mutex to lock pool
*/
mutex_t *mutex;
};
/**
+ * 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)
}
/**
+ * 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;
/**
* 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);
mysql_enumerator_t *enumerator = NULL;
conn_t *conn;
- conn = conn_get(this);
+ conn = conn_get(this, NULL);
if (!conn)
{
return NULL;
conn_t *conn;
int affected = -1;
- conn = conn_get(this);
+ conn = conn_get(this, NULL);
if (!conn)
{
return -1;
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,
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);
}
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);