[sr-dev] git:master: db_mysql Add transaction support

Olle E. Johansson oej at edvina.net
Tue Apr 2 14:13:48 CEST 2013


Module: sip-router
Branch: master
Commit: fe977dfcb940b1c48dd26fe5fe61a25aed23b96a
URL:    http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=fe977dfcb940b1c48dd26fe5fe61a25aed23b96a

Author: Olle E. Johansson <oej at edvina.net>
Committer: Olle E. Johansson <oej at edvina.net>
Date:   Tue Apr  2 14:13:01 2013 +0200

db_mysql Add transaction support

Patch contributed via Google+ by Håkon Nassjöen <haakon.nassjoen at gmail.com>

---

 modules/db_mysql/km_db_mysql.c |    3 +
 modules/db_mysql/km_dbase.c    |  136 ++++++++++++++++++++++++++++++++++++++++
 modules/db_mysql/km_dbase.h    |   16 +++++
 modules/db_mysql/km_my_con.h   |   10 ++-
 4 files changed, 161 insertions(+), 4 deletions(-)

diff --git a/modules/db_mysql/km_db_mysql.c b/modules/db_mysql/km_db_mysql.c
index 8a51f3d..88cc481 100644
--- a/modules/db_mysql/km_db_mysql.c
+++ b/modules/db_mysql/km_db_mysql.c
@@ -116,6 +116,9 @@ int db_mysql_bind_api(db_func_t *dbb)
 	dbb->insert_update    = db_mysql_insert_update;
 	dbb->insert_delayed   = db_mysql_insert_delayed;
 	dbb->affected_rows    = db_mysql_affected_rows;
+	dbb->start_transaction= db_mysql_start_transaction;
+	dbb->end_transaction  = db_mysql_end_transaction;
+	dbb->abort_transaction= db_mysql_abort_transaction;
 
 	return 0;
 }
diff --git a/modules/db_mysql/km_dbase.c b/modules/db_mysql/km_dbase.c
index 029a674..7288b66 100644
--- a/modules/db_mysql/km_dbase.c
+++ b/modules/db_mysql/km_dbase.c
@@ -500,6 +500,142 @@ int db_mysql_affected_rows(const db1_con_t* _h)
 	return (int)mysql_affected_rows(CON_CONNECTION(_h));
 }
 
+/**
+ * Starts a single transaction that will consist of one or more queries (SQL BEGIN)
+ * \param _h database handle
+ * \return 0 on success, negative on failure
+ */
+int db_mysql_start_transaction(db1_con_t* _h, db_locking_t _l)
+{
+	str begin_str = str_init("BEGIN");
+	str lock_start_str = str_init("LOCK TABLE ");
+	str lock_end_str  = str_init(" WRITE");
+	str lock_str = {0, 0};
+
+	if (!_h) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	if (CON_TRANSACTION(_h) == 1) {
+		LM_ERR("transaction already started\n");
+		return -1;
+	}
+
+	if (db_mysql_raw_query(_h, &begin_str, NULL) < 0)
+	{
+		LM_ERR("executing raw_query\n");
+		return -1;
+	}
+
+	CON_TRANSACTION(_h) = 1;
+
+	switch(_l)
+	{
+	case DB_LOCKING_NONE:
+		break;
+	case DB_LOCKING_FULL:
+		/* Fall-thru */
+	case DB_LOCKING_WRITE:
+		if ((lock_str.s = pkg_malloc((lock_start_str.len + CON_TABLE(_h)->len + lock_end_str.len) * sizeof(char))) == NULL)
+		{
+			LM_ERR("allocating pkg memory\n");
+			goto error;
+		}
+
+		memcpy(lock_str.s, lock_start_str.s, lock_start_str.len);
+		lock_str.len += lock_start_str.len;
+		memcpy(lock_str.s + lock_str.len, CON_TABLE(_h)->s, CON_TABLE(_h)->len);
+		lock_str.len += CON_TABLE(_h)->len;
+		memcpy(lock_str.s + lock_str.len, lock_end_str.s, lock_end_str.len);
+		lock_str.len += lock_end_str.len;
+
+		if (db_mysql_raw_query(_h, &lock_str, NULL) < 0)
+		{
+			LM_ERR("executing raw_query\n");
+			goto error;
+		}
+
+		if (lock_str.s) pkg_free(lock_str.s);
+		break;
+
+	default:
+		LM_WARN("unrecognised lock type\n");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	if (lock_str.s) pkg_free(lock_str.s);
+	db_mysql_abort_transaction(_h);
+	return -1;
+}
+
+/**
+ * Ends a transaction and commits the changes (SQL COMMIT)
+ * \param _h database handle
+ * \return 0 on success, negative on failure
+ */
+int db_mysql_end_transaction(db1_con_t* _h)
+{
+	str query_str = str_init("COMMIT");
+
+	if (!_h) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	if (CON_TRANSACTION(_h) == 0) {
+		LM_ERR("transaction not in progress\n");
+		return -1;
+	}
+
+	if (db_mysql_raw_query(_h, &query_str, NULL) < 0)
+	{
+		LM_ERR("executing raw_query\n");
+		return -1;
+	}
+
+	/* Only _end_ the transaction after the raw_query.  That way, if the
+ 	   raw_query fails, and the calling module does an abort_transaction()
+	   to clean-up, a ROLLBACK will be sent to the DB. */
+	CON_TRANSACTION(_h) = 0;
+	return 0;
+}
+
+/**
+ * Ends a transaction and rollsback the changes (SQL ROLLBACK)
+ * \param _h database handle
+ * \return 1 if there was something to rollback, 0 if not, negative on failure
+ */
+int db_mysql_abort_transaction(db1_con_t* _h)
+{
+	str query_str = str_init("ROLLBACK");
+
+	if (!_h) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	if (CON_TRANSACTION(_h) == 0) {
+		LM_DBG("nothing to rollback\n");
+		return 0;
+	}
+
+	/* Whether the rollback succeeds or not we need to _end_ the
+ 	   transaction now or all future starts will fail */
+	CON_TRANSACTION(_h) = 0;
+
+	if (db_mysql_raw_query(_h, &query_str, NULL) < 0)
+	{
+		LM_ERR("executing raw_query\n");
+		return -1;
+	}
+
+	return 1;
+}
+
 
 /**
   * Insert a row into a specified table, update on duplicate key.
diff --git a/modules/db_mysql/km_dbase.h b/modules/db_mysql/km_dbase.h
index 3a567f2..52b2778 100644
--- a/modules/db_mysql/km_dbase.h
+++ b/modules/db_mysql/km_dbase.h
@@ -40,6 +40,7 @@
 #include "../../lib/srdb1/db_key.h"
 #include "../../lib/srdb1/db_op.h"
 #include "../../lib/srdb1/db_val.h"
+#include "../../lib/srdb1/db_locking.h"
 #include "../../str.h"
 
 /*! \brief
@@ -118,6 +119,21 @@ int db_mysql_last_inserted_id(const db1_con_t* _h);
  */
 int db_mysql_affected_rows(const db1_con_t* _h);
 
+/*! \brief
+ * Starts transaction
+ */
+int db_mysql_start_transaction(db1_con_t* _h, db_locking_t _l);
+
+/*! \brief
+ * Commits transaction
+ */
+int db_mysql_end_transaction(db1_con_t* _h);
+
+/*! \brief
+ * Aborts transaction
+ */
+int db_mysql_abort_transaction(db1_con_t* _h);
+
 
 /*! \brief
  * Insert a row into table, update on duplicate key
diff --git a/modules/db_mysql/km_my_con.h b/modules/db_mysql/km_my_con.h
index 2624f34..33f2128 100644
--- a/modules/db_mysql/km_my_con.h
+++ b/modules/db_mysql/km_my_con.h
@@ -48,16 +48,18 @@ struct my_con {
 	MYSQL* con;              /*!< Connection representation */
 	MYSQL_ROW row;           /*!< Actual row in the result */
 	time_t timestamp;        /*!< Timestamp of last query */
+	int transaction;	 /*!< indicates whether a multi-query transaction is currently open */
 };
 
 
 /*
  * Some convenience wrappers
  */
-#define CON_RESULT(db_con)     (((struct my_con*)((db_con)->tail))->res)
-#define CON_CONNECTION(db_con) (((struct my_con*)((db_con)->tail))->con)
-#define CON_ROW(db_con)        (((struct my_con*)((db_con)->tail))->row)
-#define CON_TIMESTAMP(db_con)  (((struct my_con*)((db_con)->tail))->timestamp)
+#define CON_RESULT(db_con)      (((struct my_con*)((db_con)->tail))->res)
+#define CON_CONNECTION(db_con)  (((struct my_con*)((db_con)->tail))->con)
+#define CON_ROW(db_con)         (((struct my_con*)((db_con)->tail))->row)
+#define CON_TIMESTAMP(db_con)   (((struct my_con*)((db_con)->tail))->timestamp)
+#define CON_TRANSACTION(db_con) (((struct my_con*)((db_con)->tail))->transaction)
 
 
 /*! \brief




More information about the sr-dev mailing list