From de3ea43bf69a22ffabaf249e6ed7f6d52eb37ccd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?H=C3=A5kon=20Nessj=C3=B8en?= <haakon.nessjoen@gmail.com>
Date: Sat, 6 Apr 2013 13:10:45 +0200
Subject: [PATCH] Added query_lock support in db_mysql driver, and added support for changing table engine before creating tables with kamdbctl

---
 modules/db_mysql/km_db_mysql.c          |    1 +
 modules/db_mysql/km_dbase.c             |   60 +++++++++++++++++++++++++++++++
 modules/db_mysql/km_dbase.h             |   11 ++++++
 modules/presence/doc/presence_admin.xml |    6 +++
 utils/kamctl/kamctlrc                   |    5 +++
 utils/kamctl/kamdbctl.mysql             |   23 +++++++++---
 6 files changed, 100 insertions(+), 6 deletions(-)

diff --git a/modules/db_mysql/km_db_mysql.c b/modules/db_mysql/km_db_mysql.c
index 88cc481..712a87a 100644
--- a/modules/db_mysql/km_db_mysql.c
+++ b/modules/db_mysql/km_db_mysql.c
@@ -119,6 +119,7 @@ int db_mysql_bind_api(db_func_t *dbb)
 	dbb->start_transaction= db_mysql_start_transaction;
 	dbb->end_transaction  = db_mysql_end_transaction;
 	dbb->abort_transaction= db_mysql_abort_transaction;
+	dbb->query_lock       = db_mysql_query_lock;
 
 	return 0;
 }
diff --git a/modules/db_mysql/km_dbase.c b/modules/db_mysql/km_dbase.c
index 7288b66..6bd8e9f 100644
--- a/modules/db_mysql/km_dbase.c
+++ b/modules/db_mysql/km_dbase.c
@@ -262,6 +262,66 @@ int db_mysql_query(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _op,
 	db_mysql_val2str, db_mysql_submit_query, db_mysql_store_result);
 }
 
+/*!
+ * \brief Query table for specified rows and lock them
+ *
+ * Row locks will only work if the comparing key/values are indexes in the table.
+ * If else, the whole table will be locked.
+ *
+ * \param _h structure representing database connection
+ * \param _k key names
+ * \param _op operators
+ * \param _v values of the keys that must match
+ * \param _c column names to return
+ * \param _n nmber of key=values pairs to compare
+ * \param _nc number of columns to return
+ * \param _o order by the specified column
+ * \param _r result set
+ * \return 0 on success, negative on failure
+ */
+int db_mysql_query_lock(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _op,
+	     const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,
+	     const db_key_t _o, db1_res_t** _r)
+{
+	if (CON_TRANSACTION(_h) == 0)
+	{
+		LM_ERR("transaction not in progress\n");
+		return -1;
+	}
+	/*TODO: Legg paa "FOR UPDATE" i sql query */
+	return db_do_query_lock(_h, _k, _op, _v, _c, _n, _nc, _o, _r, db_mysql_val2str,
+		db_mysql_submit_query_lock, db_mysql_store_result);
+}
+
+/*! \brief Append exclusive locking string to query and submit it
+ *
+ * \param _h handle for the db
+ * \param _s executed query
+ * \return zero on success, negative value on failure
+ */
+int db_mysql_submit_query_lock(const db1_con_t* _h, const str* _s)
+{
+	int result;
+	str append = str_init(" FOR UPDATE");
+	str new_query;
+
+	new_query.s = pkg_malloc((_s->len + append.len) * sizeof(char));
+	if (new_query.s == NULL)
+	{
+		LM_ERR("allocating pkg memory\n");
+		return -1;
+	}
+
+	memcpy(new_query.s, _s->s, _s->len);
+	memcpy(new_query.s + _s->len, append.s, append.len);
+	new_query.len = _s->len + append.len;
+
+	result = db_mysql_submit_query(_h, (const str *)&new_query);
+	pkg_free(new_query.s);
+
+	return result;
+}
+
 /**
  * \brief Gets a partial result set, fetch rows from a result
  *
diff --git a/modules/db_mysql/km_dbase.h b/modules/db_mysql/km_dbase.h
index 52b2778..2828dd3 100644
--- a/modules/db_mysql/km_dbase.h
+++ b/modules/db_mysql/km_dbase.h
@@ -68,6 +68,17 @@ int db_mysql_query(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _op,
 	     const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,
 	     const db_key_t _o, db1_res_t** _r);
 
+/*! \brief
+ * Do a query and lock rows for update
+ */
+int db_mysql_query_lock(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _op,
+	     const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,
+	     const db_key_t _o, db1_res_t** _r);
+
+/*! \brief
+ * Append locking string to query, and submit it
+ */
+int db_mysql_submit_query_lock(const db1_con_t* _h, const str* _s);
 
 /*! \brief
  * fetch rows from a result
diff --git a/modules/presence/doc/presence_admin.xml b/modules/presence/doc/presence_admin.xml
index 02b3562..e33d0eb 100644
--- a/modules/presence/doc/presence_admin.xml
+++ b/modules/presence/doc/presence_admin.xml
@@ -30,6 +30,12 @@
 	<emphasis>subs_db_mode</emphasis> and <emphasis>publ_cache</emphasis> parameter
 	sections to decide which is the best storage configuration for you.
 	</para>
+	<para>
+	If you use a MySQL as the database engine, you should configure your presence table to
+	use InnoDB table engine because the default engine MyISAM does not support row-locks.
+	MyISAM only support table wide locks, which will decrease performance.
+	This can be defined in kamctlrc prior to running kamdbctl to create your presence tables.
+	</para>
 	<para>The module implements several API functions, that can be used by other
 	modules. In fact, it can be used only as a resource module, or "library".
 	This mode of operation is enabled if the db_url parameter is not set to any value.
diff --git a/utils/kamctl/kamctlrc b/utils/kamctl/kamctlrc
index e5d8311..54070a4 100644
--- a/utils/kamctl/kamctlrc
+++ b/utils/kamctl/kamctlrc
@@ -19,6 +19,11 @@
 # this parameter.
 # DBENGINE=MYSQL
 
+## database table engine
+# If you are using MYSQL database engine, choose which table engine to use:
+# For example; MyISAM or InnoDB
+# DBTENGINE=MyISAM
+
 ## database host
 # DBHOST=localhost
 
diff --git a/utils/kamctl/kamdbctl.mysql b/utils/kamctl/kamdbctl.mysql
index e4825ab..ae4cf27 100644
--- a/utils/kamctl/kamdbctl.mysql
+++ b/utils/kamctl/kamdbctl.mysql
@@ -33,6 +33,11 @@ if [ -z "$DBROOTUSER" ]; then
 	DBROOTUSER="root"
 fi
 
+# full privileges MySQL user
+if [ -z "$DBTENGINE" ]; then
+	DBTENGINE="MyISAM"
+fi
+
 # Uncomment this to set the database root password if you want to run this 
 # script without any user prompt. This is unsafe, but useful e.g. for 
 # automatic testing.
@@ -43,7 +48,6 @@ CMD="mysql -h $DBHOST -u$DBROOTUSER "
 DUMP_CMD="mysqldump -h $DBHOST -u$DBROOTUSER -c -t "
 #################################################################
 
-
 # read password
 prompt_pw()
 {
@@ -81,6 +85,13 @@ sql_query()
 	fi
 }
 
+# "Import" table
+# and change table engine if needed.
+import_table() # pars: <database name> <table name>
+{
+	sed -e s/ENGINE=MyISAM/ENGINE=$DBTENGINE/ $DB_SCHEMA/$2-create.sql | sql_query $1
+}
+
 
 kamailio_drop()  # pars: <database name>
 {
@@ -261,7 +272,7 @@ standard_create () # pars: <database name>
 
 	for TABLE in $STANDARD_MODULES; do
 		mdbg "Creating core table: $TABLE"
-		sql_query $1 < $DB_SCHEMA/$TABLE-create.sql
+		import_table $1 $TABLE
 		if [ $? -ne 0 ] ; then
 			merr "Creating core tables failed at $TABLE!"
 			exit 1
@@ -273,7 +284,7 @@ standard_create () # pars: <database name>
 	if [ -e $DB_SCHEMA/extensions-create.sql ]
 	then
 		minfo "Creating custom extensions tables"
-		sql_query $1 < $DB_SCHEMA/extensions-create.sql
+		import_table $1 extensions
 		if [ $? -ne 0 ] ; then
 			merr "Creating custom extensions tables failed!"
 			exit 1
@@ -293,7 +304,7 @@ presence_create () # pars: <database name>
 
 	for TABLE in $PRESENCE_MODULES; do
 		mdbg "Creating presence tables for $TABLE"
-		sql_query $1 < $DB_SCHEMA/$TABLE-create.sql
+		import_table $1 $TABLE
 		if [ $? -ne 0 ] ; then
 			merr "Creating presence tables failed at $TABLE!"
 			exit 1
@@ -315,7 +326,7 @@ extra_create () # pars: <database name>
 
 	for TABLE in $EXTRA_MODULES; do
 		mdbg "Creating extra table: $TABLE"
-		sql_query $1 < $DB_SCHEMA/$TABLE-create.sql
+		import_table $1 $TABLE
 		if [ $? -ne 0 ] ; then
 			merr "Creating extra tables failed at $TABLE!"
 			exit 1
@@ -335,7 +346,7 @@ dbuid_create () # pars: <database name>
 
 	for TABLE in $DBUID_MODULES; do
 		mdbg "Creating uid table: $TABLE"
-		sql_query $1 < $DB_SCHEMA/$TABLE-create.sql
+		import_table $1 $TABLE
 		if [ $? -ne 0 ] ; then
 			merr "Creating uid tables failed at $TABLE!"
 			exit 1
-- 
1.7.1

