mysql: Implement transaction handling
This commit is contained in:
@@ -50,6 +50,11 @@ struct private_mysql_database_t {
|
|||||||
*/
|
*/
|
||||||
linked_list_t *pool;
|
linked_list_t *pool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* thread-specific transaction, as transaction_t
|
||||||
|
*/
|
||||||
|
thread_value_t *transaction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mutex to lock pool
|
* mutex to lock pool
|
||||||
*/
|
*/
|
||||||
@@ -99,6 +104,28 @@ struct conn_t {
|
|||||||
bool in_use;
|
bool in_use;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Release a mysql connection
|
||||||
*/
|
*/
|
||||||
@@ -109,6 +136,16 @@ static void conn_release(private_mysql_database_t *this, conn_t *conn)
|
|||||||
this->mutex->unlock(this->mutex);
|
this->mutex->unlock(this->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 specific initialization flag
|
||||||
*/
|
*/
|
||||||
@@ -161,13 +198,24 @@ static void conn_destroy(conn_t *this)
|
|||||||
/**
|
/**
|
||||||
* Acquire/Reuse a mysql connection
|
* 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;
|
conn_t *current, *found = NULL;
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
|
transaction_t *transaction;
|
||||||
|
|
||||||
thread_initialize();
|
thread_initialize();
|
||||||
|
|
||||||
|
transaction = this->transaction->get(this->transaction);
|
||||||
|
if (transaction)
|
||||||
|
{
|
||||||
|
if (trans)
|
||||||
|
{
|
||||||
|
*trans = transaction;
|
||||||
|
}
|
||||||
|
return transaction->conn;
|
||||||
|
}
|
||||||
|
|
||||||
while (TRUE)
|
while (TRUE)
|
||||||
{
|
{
|
||||||
this->mutex->lock(this->mutex);
|
this->mutex->lock(this->mutex);
|
||||||
@@ -490,7 +538,7 @@ METHOD(database_t, query, enumerator_t*,
|
|||||||
mysql_enumerator_t *enumerator = NULL;
|
mysql_enumerator_t *enumerator = NULL;
|
||||||
conn_t *conn;
|
conn_t *conn;
|
||||||
|
|
||||||
conn = conn_get(this);
|
conn = conn_get(this, NULL);
|
||||||
if (!conn)
|
if (!conn)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -582,7 +630,7 @@ METHOD(database_t, execute, int,
|
|||||||
conn_t *conn;
|
conn_t *conn;
|
||||||
int affected = -1;
|
int affected = -1;
|
||||||
|
|
||||||
conn = conn_get(this);
|
conn = conn_get(this, NULL);
|
||||||
if (!conn)
|
if (!conn)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@@ -606,19 +654,81 @@ METHOD(database_t, execute, int,
|
|||||||
METHOD(database_t, transaction, bool,
|
METHOD(database_t, transaction, bool,
|
||||||
private_mysql_database_t *this)
|
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,
|
METHOD(database_t, commit, bool,
|
||||||
private_mysql_database_t *this)
|
private_mysql_database_t *this)
|
||||||
{
|
{
|
||||||
return FALSE;
|
return finalize_transaction(this, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(database_t, rollback, bool,
|
METHOD(database_t, rollback, bool,
|
||||||
private_mysql_database_t *this)
|
private_mysql_database_t *this)
|
||||||
{
|
{
|
||||||
return FALSE;
|
return finalize_transaction(this, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(database_t, get_driver,db_driver_t,
|
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,
|
METHOD(database_t, destroy, void,
|
||||||
private_mysql_database_t *this)
|
private_mysql_database_t *this)
|
||||||
{
|
{
|
||||||
|
this->transaction->destroy(this->transaction);
|
||||||
this->pool->destroy_function(this->pool, (void*)conn_destroy);
|
this->pool->destroy_function(this->pool, (void*)conn_destroy);
|
||||||
this->mutex->destroy(this->mutex);
|
this->mutex->destroy(this->mutex);
|
||||||
free(this->host);
|
free(this->host);
|
||||||
@@ -721,9 +832,10 @@ mysql_database_t *mysql_database_create(char *uri)
|
|||||||
}
|
}
|
||||||
this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
|
this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
|
||||||
this->pool = linked_list_create();
|
this->pool = linked_list_create();
|
||||||
|
this->transaction = thread_value_create(NULL);
|
||||||
|
|
||||||
/* check connectivity */
|
/* check connectivity */
|
||||||
conn = conn_get(this);
|
conn = conn_get(this, NULL);
|
||||||
if (!conn)
|
if (!conn)
|
||||||
{
|
{
|
||||||
destroy(this);
|
destroy(this);
|
||||||
|
|||||||
Reference in New Issue
Block a user