- rename database modules to use the 'db_' prefix
authorHenning Westerholt <henning.westerholt@1und1.de>
Wed, 6 Feb 2008 13:10:03 +0000 (13:10 +0000)
committerHenning Westerholt <henning.westerholt@1und1.de>
Wed, 6 Feb 2008 13:10:03 +0000 (13:10 +0000)
git-svn-id: https://openser.svn.sourceforge.net/svnroot/openser/trunk@3638 689a6050-402a-0410-94f2-e92a70836424

19 files changed:
modules/db_mysql/km_Makefile [new file with mode: 0644]
modules/db_mysql/km_README [new file with mode: 0644]
modules/db_mysql/km_db_mod.c [new file with mode: 0644]
modules/db_mysql/km_db_mod.h [new file with mode: 0644]
modules/db_mysql/km_dbase.c [new file with mode: 0644]
modules/db_mysql/km_dbase.h [new file with mode: 0644]
modules/db_mysql/km_doc/mysql.sgml [new file with mode: 0644]
modules/db_mysql/km_doc/mysql_devel.sgml [new file with mode: 0644]
modules/db_mysql/km_doc/mysql_faq.sgml [new file with mode: 0644]
modules/db_mysql/km_doc/mysql_parser.dia [new file with mode: 0644]
modules/db_mysql/km_doc/mysql_user.sgml [new file with mode: 0644]
modules/db_mysql/km_my_con.c [new file with mode: 0644]
modules/db_mysql/km_my_con.h [new file with mode: 0644]
modules/db_mysql/km_res.c [new file with mode: 0644]
modules/db_mysql/km_res.h [new file with mode: 0644]
modules/db_mysql/km_row.c [new file with mode: 0644]
modules/db_mysql/km_row.h [new file with mode: 0644]
modules/db_mysql/km_val.c [new file with mode: 0644]
modules/db_mysql/km_val.h [new file with mode: 0644]

diff --git a/modules/db_mysql/km_Makefile b/modules/db_mysql/km_Makefile
new file mode 100644 (file)
index 0000000..cb8d362
--- /dev/null
@@ -0,0 +1,41 @@
+# $Id$
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=mysql.so
+
+# set CROSS_COMPILE to true if you want to skip
+# the autodetection
+# CROSS_COMPILE=true
+
+ifeq ($(CROSS_COMPILE),)
+MYSQLCFG=$(shell which mysql_config)
+endif
+
+ifneq ($(MYSQLCFG),)
+
+       # use autodetection
+       DEFS += $(shell $(MYSQLCFG) --include | sed 's/\(-I.*\)\/mysql/\1/g' )
+       LIBS = $(shell $(MYSQLCFG) --libs)
+
+else
+
+       # use standard know paths
+       # mysql.h locations (freebsd,openbsd  solaris)
+       DEFS +=-I$(LOCALBASE)/include -I$(LOCALBASE)/include/mysql \
+               -I$(LOCALBASE)/mysql/include/mysql -I$(LOCALBASE)/mysql/include \
+               -I$(SYSBASE)/include/mysql
+
+       # libmysqlclient locations on RH/Suse, Solaris /OpenBSD, FreeBSD
+       # (Debian does the right thing and puts it in /usr/lib)
+       LIBS=-L$(SYSBASE)/lib/mysql -L$(LOCALBASE)/lib -L$(LOCALBASE)/lib/mysql \
+               -L$(LOCALBASE)/mysql/lib/mysql/ \
+               -L$(LOCALBASE)/mysql/lib \
+               -L$(SYSBASE)/lib64/mysql \
+               -lm -lmysqlclient -lz
+
+endif
+
+include ../../Makefile.modules
diff --git a/modules/db_mysql/km_README b/modules/db_mysql/km_README
new file mode 100644 (file)
index 0000000..9ffe9b0
--- /dev/null
@@ -0,0 +1,168 @@
+mysql Module
+
+Daniel-Constantin Mierla
+
+   voice-system.ro
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   Copyright © 2006 voice-system.ro
+     __________________________________________________________
+
+   Table of Contents
+   1. User's Guide
+
+        1.1. Overview
+        1.2. Dependencies
+
+              1.2.1. OpenSER Modules
+              1.2.2. External Libraries or Applications
+
+        1.3. Exported Parameters
+
+              1.3.1. ping_interval (integer)
+              1.3.2. timeout_interval (integer)
+              1.3.3. auto_reconnect (integer)
+
+        1.4. Exported Functions
+        1.5. Installation
+
+   2. Developer's Guide
+   3. Frequently Asked Questions
+
+   List of Examples
+   1-1. Set ping_interval parameter
+   1-2. Set timeout_interval parameter
+   1-3. Set auto_reconnect parameter
+     __________________________________________________________
+
+Chapter 1. User's Guide
+
+1.1. Overview
+
+   This is a module which provides MySQL connectivity for OpenSER.
+   It implements the DB API defined in OpenSER.
+     __________________________________________________________
+
+1.2. Dependencies
+
+1.2.1. OpenSER Modules
+
+   The following modules must be loaded before this module:
+
+     * No dependencies on other OpenSER modules.
+     __________________________________________________________
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed
+   before running OpenSER with this module loaded:
+
+     * libmysqlclient-dev - the development libraries of
+       mysql-client.
+     __________________________________________________________
+
+1.3. Exported Parameters
+
+1.3.1. ping_interval (integer)
+
+   Time interval to send ping messages to MySQL server in order to
+   keep the connection open.
+
+   Default value is 300 (5 min).
+
+   Example 1-1. Set ping_interval parameter
+...
+modparam("mysql", "ping_interval", 600)
+...
+     __________________________________________________________
+
+1.3.2. timeout_interval (integer)
+
+   Time interval after that an connection attempt, read or write
+   request is aborted. The value counts three times, as several
+   retries are done from the driver before it gives up.
+
+   The read timeout parameter is ignored on driver versions prior
+   to "5.1.12", "5.0.25" and "4.1.22". The write timeout parameter
+   is ignored on version prior to "5.1.12" and "5.0.25", the "4.1"
+   release don't support it at all.
+
+   Default value is 2 (6 sec).
+
+   Example 1-2. Set timeout_interval parameter
+...
+modparam("mysql", "timeout_interval", 2)
+...
+     __________________________________________________________
+
+1.3.3. auto_reconnect (integer)
+
+   Configure the module to auto reconnect to MySQL server if the
+   connection was lost.
+
+   Default value is 1 (1 - on / 0 - off).
+
+   Example 1-3. Set auto_reconnect parameter
+...
+modparam("auto_reconnect", "auto_reconnect", 0)
+...
+     __________________________________________________________
+
+1.4. Exported Functions
+
+   No function exported to be used from configuration file.
+     __________________________________________________________
+
+1.5. Installation
+
+   Because it dependes on an external library, the mysql module is
+   not compiled and installed by default. You can use one of the
+   next options.
+
+     * - edit the "Makefile" and remove "mysql" from
+       "excluded_modules" list. Then follow the standard procedure
+       to install OpenSER: "make all; make install".
+     * - from command line use: 'make all include_modules="mysql";
+       make install include_modules="mysql"'.
+     __________________________________________________________
+
+Chapter 2. Developer's Guide
+
+   The module does not provide any API to use in other OpenSER
+   modules.
+     __________________________________________________________
+
+Chapter 3. Frequently Asked Questions
+
+   3.1. Where can I find more about OpenSER?
+   3.2. Where can I post a question about this module?
+   3.3. How can I report a bug?
+
+   3.1. Where can I find more about OpenSER?
+
+   Take a look at http://openser.org/.
+
+   3.2. Where can I post a question about this module?
+
+   First at all check if your question was already answered on one
+   of our mailing lists:
+
+     * User Mailing List -
+       http://openser.org/cgi-bin/mailman/listinfo/users
+     * Developer Mailing List -
+       http://openser.org/cgi-bin/mailman/listinfo/devel
+
+   E-mails regarding any stable OpenSER release should be sent to
+   <users@openser.org> and e-mails regarding development versions
+   should be sent to <devel@openser.org>.
+
+   If you want to keep the mail private, send it to
+   <team@openser.org>.
+
+   3.3. How can I report a bug?
+
+   Please follow the guidelines provided at:
+   http://sourceforge.net/tracker/?group_id=139143.
diff --git a/modules/db_mysql/km_db_mod.c b/modules/db_mysql/km_db_mod.c
new file mode 100644 (file)
index 0000000..cd60948
--- /dev/null
@@ -0,0 +1,99 @@
+/* 
+ * $Id$ 
+ *
+ * MySQL module interface
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/*
+ * History:
+ * --------
+ *  2003-03-11  updated to the new module exports interface (andrei)
+ *  2003-03-16  flags export parameter added (janakj)
+ */
+
+#include "../../sr_module.h"
+#include "dbase.h"
+#include "db_mod.h"
+
+#include <mysql/mysql.h>
+
+unsigned int db_mysql_ping_interval = 5 * 60; /* Default is 5 minutes */
+unsigned int db_mysql_timeout_interval = 2;   /* Default is 6 seconds */
+unsigned int db_mysql_auto_reconnect = 1;     /* Default is enabled   */
+
+static int mysql_mod_init(void);
+
+MODULE_VERSION
+
+
+/*
+ * MySQL database module interface
+ */
+static cmd_export_t cmds[] = {
+       {"db_use_table",        (cmd_function)db_mysql_use_table,     2, 0, 0, 0},
+       {"db_init",             (cmd_function)db_mysql_init,          1, 0, 0, 0},
+       {"db_close",            (cmd_function)db_mysql_close,         2, 0, 0, 0},
+       {"db_query",            (cmd_function)db_mysql_query,         2, 0, 0, 0},
+       {"db_fetch_result",     (cmd_function)db_mysql_fetch_result,  2, 0, 0, 0},
+       {"db_raw_query",        (cmd_function)db_mysql_raw_query,     2, 0, 0, 0},
+       {"db_free_result",      (cmd_function)db_mysql_free_result,   2, 0, 0, 0},
+       {"db_insert",           (cmd_function)db_mysql_insert,        2, 0, 0, 0},
+       {"db_delete",           (cmd_function)db_mysql_delete,        2, 0, 0, 0},
+       {"db_update",           (cmd_function)db_mysql_update,        2, 0, 0, 0},
+       {"db_replace",          (cmd_function)db_mysql_replace,       2, 0, 0, 0},
+       {"db_last_inserted_id", (cmd_function)db_last_inserted_id,    1, 0, 0, 0},
+       {"db_insert_update",    (cmd_function)db_insert_update,       2, 0, 0, 0},
+       {0, 0, 0, 0, 0, 0}
+};
+
+
+/*
+ * Exported parameters
+ */
+static param_export_t params[] = {
+       {"ping_interval",    INT_PARAM, &db_mysql_ping_interval},
+       {"timeout_interval", INT_PARAM, &db_mysql_timeout_interval},
+       {"auto_reconnect",   INT_PARAM, &db_mysql_auto_reconnect},
+       {0, 0, 0}
+};
+
+
+struct module_exports exports = {      
+       "mysql",
+       DEFAULT_DLFLAGS, /* dlopen flags */
+       cmds,
+       params,          /*  module parameters */
+       0,               /* exported statistics */
+       0,               /* exported MI functions */
+       0,               /* exported pseudo-variables */
+       0,               /* extra processes */
+       mysql_mod_init,  /* module initialization function */
+       0,               /* response function*/
+       0,               /* destroy function */
+       0                /* per-child init function */
+};
+
+
+static int mysql_mod_init(void)
+{
+       LM_DBG("mysql: MySQL client version is %s\n", mysql_get_client_info());
+       return 0;
+}
diff --git a/modules/db_mysql/km_db_mod.h b/modules/db_mysql/km_db_mod.h
new file mode 100644 (file)
index 0000000..1d5c9c9
--- /dev/null
@@ -0,0 +1,38 @@
+/* 
+ * $Id$ 
+ *
+ * MySQL module interface
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ *  2003-03-11  updated to the new module exports interface (andrei)
+ *  2003-03-16  flags export parameter added (janakj)
+ */
+
+#ifndef DB_MOD_H
+#define DB_MOD_H
+
+extern unsigned int db_mysql_ping_interval;
+extern unsigned int db_mysql_timeout_interval;
+extern unsigned int db_mysql_auto_reconnect;
+
+#endif /* DB_MOD_H */
diff --git a/modules/db_mysql/km_dbase.c b/modules/db_mysql/km_dbase.c
new file mode 100644 (file)
index 0000000..18a6504
--- /dev/null
@@ -0,0 +1,489 @@
+/* 
+ * $Id$ 
+ *
+ * MySQL module core functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * \class mysql/dbase.c
+ * \brief Implementation of core functions for the MySQL driver.
+ *
+ * This file contains the implementation of core functions for the MySQL
+ * database driver, for example to submit a query or fetch a result.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <mysql/mysql.h>
+#include <mysql/errmsg.h>
+#include <mysql/mysql_version.h>
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../db/db_query.h"
+#include "../../db/db_ut.h"
+#include "val.h"
+#include "my_con.h"
+#include "res.h"
+#include "row.h"
+#include "db_mod.h"
+#include "dbase.h"
+
+
+/**
+ * \brief Send a SQL query to the server.
+ *
+ * Send a SQL query to the database server. This methods tries to reconnect
+ * to the server if the connection is gone and the auto_reconnect parameter is
+ * enabled. It also issues a mysql_ping before the query to connect again after
+ * a long waiting period because for some older mysql versions the auto reconnect
+ * don't work sufficient. If auto_reconnect is enabled and the server supports it,
+ * then the mysql_ping is probably not necessary, but its safer to do it in this
+ * cases too.
+ *
+ * \param _h handle for the db
+ * \param _s executed query
+ * \return zero on success, negative value on failure
+ */
+static int db_mysql_submit_query(const db_con_t* _h, const str* _s)
+{      
+       time_t t;
+       int i, code;
+
+       if (!_h || !_s || !_s->s) {
+               LM_ERR("invalid parameter value\n");
+               return -1;
+       }
+
+       if (db_mysql_ping_interval) {
+               t = time(0);
+               if ((t - CON_TIMESTAMP(_h)) > db_mysql_ping_interval) {
+                       if (mysql_ping(CON_CONNECTION(_h))) {
+                               LM_WARN("driver error on ping: %s\n", mysql_error(CON_CONNECTION(_h)));
+                       }
+               }
+               /*
+                * We're doing later a query anyway that will reset the timout of the server,
+                * so it makes sense to set the timestamp value to the actual time in order
+                * to prevent unnecessary pings.
+                */
+               CON_TIMESTAMP(_h) = t;
+       }
+
+       /* screws up the terminal when the query contains a BLOB :-( (by bogdan)
+        * LM_DBG("submit_query(): %.*s\n", _s->len, _s->s);
+        */
+
+       /* When a server connection is lost and a query is attempted, most of
+        * the time the query will return a CR_SERVER_LOST, then at the second
+        * attempt to execute it, the mysql lib will reconnect and succeed.
+        * However is a few cases, the first attempt returns CR_SERVER_GONE_ERROR
+        * the second CR_SERVER_LOST and only the third succeeds.
+        * Thus the 3 in the loop count. Increasing the loop count over this
+        * value shouldn't be needed, but it doesn't hurt either, since the loop
+        * will most of the time stop at the second or sometimes at the third
+        * iteration.
+        */
+       for (i=0; i < (db_mysql_auto_reconnect ? 3 : 1); i++) {
+               if (mysql_real_query(CON_CONNECTION(_h), _s->s, _s->len) == 0) {
+                       return 0;
+               }
+               code = mysql_errno(CON_CONNECTION(_h));
+               if (code != CR_SERVER_GONE_ERROR && code != CR_SERVER_LOST) {
+                       break;
+               }
+       }
+       LM_ERR("driver error on query: %s\n", mysql_error(CON_CONNECTION(_h)));
+       return -2;
+}
+
+
+
+/*
+ * Initialize database module
+ * No function should be called before this
+ */
+db_con_t* db_mysql_init(const str* _url)
+{
+       return db_do_init(_url, (void *)db_mysql_new_connection);
+}
+
+
+/*
+ * Shut down database module
+ * No function should be called after this
+ */
+void db_mysql_close(db_con_t* _h)
+{
+       db_do_close(_h, db_mysql_free_connection);
+}
+
+
+/*
+ * Retrieve result set
+ */
+static int db_mysql_store_result(const db_con_t* _h, db_res_t** _r)
+{
+       if ((!_h) || (!_r)) {
+               LM_ERR("invalid parameter value\n");
+               return -1;
+       }
+
+       *_r = db_new_result();
+       if (*_r == 0) {
+               LM_ERR("no memory left\n");
+               return -2;
+       }
+
+       CON_RESULT(_h) = mysql_store_result(CON_CONNECTION(_h));
+       if (!CON_RESULT(_h)) {
+               if (mysql_field_count(CON_CONNECTION(_h)) == 0) {
+                       (*_r)->col.n = 0;
+                       (*_r)->n = 0;
+                       goto done;
+               } else {
+                       LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h)));
+                       db_free_result(*_r);
+                       *_r = 0;
+                       return -3;
+               }
+       }
+
+       if (db_mysql_convert_result(_h, *_r) < 0) {
+               LM_ERR("error while converting result\n");
+               pkg_free(*_r);
+               *_r = 0;
+               /* all mem on openser API side is already freed by
+                * db_mysql_convert_result in case of error, but we also need
+                * to free the mem from the mysql lib side */
+               mysql_free_result(CON_RESULT(_h));
+#if (MYSQL_VERSION_ID >= 40100)
+               while( mysql_next_result( CON_CONNECTION(_h) ) > 0 ) {
+                       MYSQL_RES *res = mysql_store_result( CON_CONNECTION(_h) );
+                       mysql_free_result(res);
+               }
+#endif
+               CON_RESULT(_h) = 0;
+               return -4;
+       }
+
+done:
+#if (MYSQL_VERSION_ID >= 40100)
+       while( mysql_next_result( CON_CONNECTION(_h) ) > 0 ) {
+               MYSQL_RES *res = mysql_store_result( CON_CONNECTION(_h) );
+               mysql_free_result(res);
+       }
+#endif
+
+       return 0;
+}
+
+
+/*
+ * Release a result set from memory
+ */
+int db_mysql_free_result(db_con_t* _h, db_res_t* _r)
+{
+     if ((!_h) || (!_r)) {
+            LM_ERR("invalid parameter value\n");
+            return -1;
+     }
+
+     if (db_free_result(_r) < 0) {
+            LM_ERR("unable to free result structure\n");
+            return -1;
+     }
+     mysql_free_result(CON_RESULT(_h));
+     CON_RESULT(_h) = 0;
+     return 0;
+}
+
+
+/*
+ * Query table for specified rows
+ * _h: structure representing database connection
+ * _k: key names
+ * _op: operators
+ * _v: values of the keys that must match
+ * _c: column names to return
+ * _n: number of key=values pairs to compare
+ * _nc: number of columns to return
+ * _o: order by the specified column
+ */
+int db_mysql_query(const db_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, db_res_t** _r)
+{
+       return db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, _r,
+       db_mysql_val2str, db_mysql_submit_query, db_mysql_store_result);
+}
+
+/*
+ * gets a partial result set
+ * _h: structure representing the database connection
+ * _r: pointer to a structure representing the result
+ * nrows: number of fetched rows
+ */
+int db_mysql_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows)
+{
+       int n;
+       int i;
+
+       if (!_h || !_r || nrows < 0) {
+               LM_ERR("Invalid parameter value\n");
+               return -1;
+       }
+
+       /* exit if the fetch count is zero */
+       if (nrows == 0) {
+               db_free_result(*_r);
+               *_r = 0;
+               return 0;
+       }
+
+       if(*_r==0) {
+               /* Allocate a new result structure */
+               *_r = db_new_result();
+               if (*_r == 0) {
+                       LM_ERR("no memory left\n");
+                       return -2;
+               }
+
+               CON_RESULT(_h) = mysql_store_result(CON_CONNECTION(_h));
+               if (!CON_RESULT(_h)) {
+                       if (mysql_field_count(CON_CONNECTION(_h)) == 0) {
+                               (*_r)->col.n = 0;
+                               (*_r)->n = 0;
+                               return 0;
+                       } else {
+                               LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h)));
+                               db_free_result(*_r);
+                               *_r = 0;
+                               return -3;
+                       }
+               }
+               if (db_mysql_get_columns(_h, *_r) < 0) {
+                       LM_ERR("error while getting column names\n");
+                       return -4;
+               }
+
+               RES_NUM_ROWS(*_r) = mysql_num_rows(CON_RESULT(_h));
+               if (!RES_NUM_ROWS(*_r)) {
+                       RES_ROWS(*_r) = 0;
+                       return 0;
+               }
+       } else {
+               /* free old rows */
+               if(RES_ROWS(*_r)!=0)
+                       db_free_rows(*_r);
+               RES_ROWS(*_r) = 0;
+               RES_ROW_N(*_r) = 0;
+       }
+
+       /* determine the number of rows remaining to be processed */
+       n = RES_NUM_ROWS(*_r) - RES_LAST_ROW(*_r);
+
+       /* If there aren't any more rows left to process, exit */
+       if(n<=0)
+               return 0;
+
+       /* if the fetch count is less than the remaining rows to process                 */
+       /* set the number of rows to process (during this call) equal to the fetch count */
+       if(nrows < n)
+               n = nrows;
+
+       RES_LAST_ROW(*_r) += n;
+       RES_ROW_N(*_r) = n;
+
+       RES_ROWS(*_r) = (struct db_row*)pkg_malloc(sizeof(db_row_t) * n);
+       if (!RES_ROWS(*_r)) {
+               LM_ERR("no memory left\n");
+               return -5;
+       }
+
+       for(i = 0; i < n; i++) {
+               CON_ROW(_h) = mysql_fetch_row(CON_RESULT(_h));
+               if (!CON_ROW(_h)) {
+                       LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h)));
+                       RES_ROW_N(*_r) = i;
+                       db_free_rows(*_r);
+                       return -6;
+               }
+               if (db_mysql_convert_row(_h, *_r, &(RES_ROWS(*_r)[i])) < 0) {
+                       LM_ERR("error while converting row #%d\n", i);
+                       RES_ROW_N(*_r) = i;
+                       db_free_rows(*_r);
+                       return -7;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Execute a raw SQL query
+ */
+int db_mysql_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r)
+{
+       return db_do_raw_query(_h, _s, _r, db_mysql_submit_query,
+       db_mysql_store_result);
+}
+
+
+/*
+ * Insert a row into specified table
+ * _h: structure representing database connection
+ * _k: key names
+ * _v: values of the keys
+ * _n: number of key=value pairs
+ */
+int db_mysql_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n)
+{
+       return db_do_insert(_h, _k, _v, _n, db_mysql_val2str,
+       db_mysql_submit_query);
+}
+
+
+/*
+ * Delete a row from the specified table
+ * _h: structure representing database connection
+ * _k: key names
+ * _o: operators
+ * _v: values of the keys that must match
+ * _n: number of key=value pairs
+ */
+int db_mysql_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+       const db_val_t* _v, const int _n)
+{
+       return db_do_delete(_h, _k, _o, _v, _n, db_mysql_val2str,
+       db_mysql_submit_query);
+}
+
+
+/*
+ * Update some rows in the specified table
+ * _h: structure representing database connection
+ * _k: key names
+ * _o: operators
+ * _v: values of the keys that must match
+ * _uk: updated columns
+ * _uv: updated values of the columns
+ * _n: number of key=value pairs
+ * _un: number of columns to update
+ */
+int db_mysql_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o, 
+       const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, 
+       const int _un)
+{
+       return db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_mysql_val2str,
+       db_mysql_submit_query);
+}
+
+
+/*
+ * Just like insert, but replace the row if it exists
+ */
+int db_mysql_replace(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n)
+{
+       return db_do_replace(_h, _k, _v, _n, db_mysql_val2str,
+       db_mysql_submit_query);
+}
+
+
+/*
+ * Returns the last inserted ID
+ */
+int db_last_inserted_id(const db_con_t* _h)
+{
+       if (!_h) {
+               LM_ERR("invalid parameter value\n");
+               return -1;
+       }
+       return mysql_insert_id(CON_CONNECTION(_h));
+}
+
+ /*
+  * Insert a row into specified table, update on duplicate key
+  * _h: structure representing database connection
+  * _k: key names
+  * _v: values of the keys
+  * _n: number of key=value pairs
+ */
+ int db_insert_update(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v,
+       const int _n)
+ {
+       int off, ret;
+       static str  sql_str;
+       static char sql_buf[SQL_BUF_LEN];
+       if ((!_h) || (!_k) || (!_v) || (!_n)) {
+               LM_ERR("invalid parameter value\n");
+               return -1;
+       }
+       ret = snprintf(sql_buf, SQL_BUF_LEN, "insert into %.*s (", CON_TABLE(_h)->len, CON_TABLE(_h)->s);
+       if (ret < 0 || ret >= SQL_BUF_LEN) goto error;
+       off = ret;
+
+       ret = db_print_columns(sql_buf + off, SQL_BUF_LEN - off, _k, _n);
+       if (ret < 0) return -1;
+       off += ret;
+
+       ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values (");
+       if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error;
+       off += ret;
+       ret = db_print_values(_h, sql_buf + off, SQL_BUF_LEN - off, _v, _n, db_mysql_val2str);
+       if (ret < 0) return -1;
+       off += ret;
+
+       *(sql_buf + off++) = ')';
+       
+       ret = snprintf(sql_buf + off, SQL_BUF_LEN - off, " on duplicate key update ");
+       if (ret < 0 || ret >= (SQL_BUF_LEN - off)) goto error;
+       off += ret;
+       
+       ret = db_print_set(_h, sql_buf + off, SQL_BUF_LEN - off, _k, _v, _n, db_mysql_val2str);
+       if (ret < 0) return -1;
+       off += ret;
+       
+       sql_str.s = sql_buf;
+       sql_str.len = off;
+       if (db_mysql_submit_query(_h, &sql_str) < 0) {
+               LM_ERR("error while submitting query\n");
+               return -2;
+       }
+       return 0;
+
+error:
+       LM_ERR("error while preparing insert_update operation\n");
+       return -1;
+}
+
+
+/*
+ * Store name of table that will be used by
+ * subsequent database functions
+ */
+int db_mysql_use_table(db_con_t* _h, const str* _t)
+{
+       return db_use_table(_h, _t);
+}
diff --git a/modules/db_mysql/km_dbase.h b/modules/db_mysql/km_dbase.h
new file mode 100644 (file)
index 0000000..cb651fa
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * $Id$
+ *
+ * MySQL module core functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef DBASE_H
+#define DBASE_H
+
+
+#include "../../db/db_con.h"
+#include "../../db/db_res.h"
+#include "../../db/db_key.h"
+#include "../../db/db_op.h"
+#include "../../db/db_val.h"
+#include "../../str.h"
+
+/*
+ * Initialize database connection
+ */
+db_con_t* db_mysql_init(const str* _sqlurl);
+
+
+/*
+ * Close a database connection
+ */
+void db_mysql_close(db_con_t* _h);
+
+
+/*
+ * Free all memory allocated by get_result
+ */
+int db_mysql_free_result(db_con_t* _h, db_res_t* _r);
+
+
+/*
+ * Do a query
+ */
+int db_mysql_query(const db_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, db_res_t** _r);
+
+
+/*
+ * fetch rows from a result
+ */
+int db_mysql_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows);
+
+
+/*
+ * Raw SQL query
+ */
+int db_mysql_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r);
+
+
+/*
+ * Insert a row into table
+ */
+int db_mysql_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n);
+
+
+/*
+ * Delete a row from table
+ */
+int db_mysql_delete(const db_con_t* _h, const db_key_t* _k, const 
+       db_op_t* _o, const db_val_t* _v, const int _n);
+
+
+/*
+ * Update a row in table
+ */
+int db_mysql_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+       const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n,
+       const int _un);
+
+
+/*
+ * Just like insert, but replace the row if it exists
+ */
+int db_mysql_replace(const db_con_t* handle, const db_key_t* keys, const db_val_t*     vals, const int n);
+
+/*
+ * Returns the last inserted ID
+ */
+int db_last_inserted_id(const db_con_t* _h);
+
+/*
+ * Insert a row into table, update on duplicate key
+ */
+int db_insert_update(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v,
+       const int _n);
+
+
+/*
+ * Store name of table that will be used by
+ * subsequent database functions
+ */
+int db_mysql_use_table(db_con_t* _h, const str* _t);
+
+
+#endif /* DBASE_H */
diff --git a/modules/db_mysql/km_doc/mysql.sgml b/modules/db_mysql/km_doc/mysql.sgml
new file mode 100644 (file)
index 0000000..cd4d6ac
--- /dev/null
@@ -0,0 +1,55 @@
+<!DOCTYPE Book PUBLIC "-//OASIS//DTD DocBook V4.2//EN" [
+
+
+<!ENTITY user SYSTEM "mysql_user.sgml">
+<!ENTITY devel SYSTEM "mysql_devel.sgml">
+<!ENTITY faq SYSTEM "mysql_faq.sgml">
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../doc/entities.sgml">
+%docentities;
+
+]>
+
+<book>
+    <bookinfo>
+       <title>mysql Module</title>
+       <productname class="trade">&sername;</productname>
+       <authorgroup>
+           <author>
+               <firstname>Daniel-Constantin</firstname>
+               <surname>Mierla</surname>
+               <affiliation><orgname>&voicesystem;</orgname></affiliation>
+               <address>
+               <email>daniel@voice-system.ro</email>
+               <otheraddr>
+               <ulink url="http://www.voice-system.ro">http://www.voice-system.ro</ulink>
+               </otheraddr>
+               </address>
+           </author>
+           <editor>
+               <firstname>Daniel-Constantin</firstname>
+               <surname>Mierla</surname>
+               <address>
+                   <email>danile@voice-system.ro</email>
+               </address>
+           </editor>
+       </authorgroup>
+       <copyright>
+           <year>2006</year>
+           <holder>&voicesystem;</holder>
+       </copyright>
+       <revhistory>
+           <revision>
+               <revnumber>$Revision$</revnumber>
+               <date>$Date$</date>
+           </revision>
+       </revhistory>
+    </bookinfo>
+    <toc></toc>
+    
+    &user;
+    &devel;
+    &faq;
+    
+</book>
diff --git a/modules/db_mysql/km_doc/mysql_devel.sgml b/modules/db_mysql/km_doc/mysql_devel.sgml
new file mode 100644 (file)
index 0000000..f7c2855
--- /dev/null
@@ -0,0 +1,22 @@
+<!-- Module Developer's Guide -->
+
+<chapter>
+    <chapterinfo>
+       <revhistory>
+           <revision>
+               <revnumber>$Revision$</revnumber>
+               <date>$Date$</date>
+           </revision>
+       </revhistory>
+    </chapterinfo>
+    <title>Developer's Guide</title>
+    <para>
+       The module does not provide any <acronym>API</acronym> to use in other &ser; modules.
+    </para>
+</chapter>
+
+<!-- Keep this element at the end of the file
+Local Variables:
+sgml-parent-document: ("mysql.sgml" "book" "chapter")
+End:
+-->
diff --git a/modules/db_mysql/km_doc/mysql_faq.sgml b/modules/db_mysql/km_doc/mysql_faq.sgml
new file mode 100644 (file)
index 0000000..94ad10b
--- /dev/null
@@ -0,0 +1,70 @@
+<!-- Module FAQ -->
+
+<chapter>
+    <chapterinfo>
+       <revhistory>
+           <revision>
+               <revnumber>$Revision$</revnumber>
+               <date>$Date$</date>
+           </revision>
+       </revhistory>
+    </chapterinfo>
+    <title>Frequently Asked Questions</title>
+    <qandaset defaultlabel="number">
+       <qandaentry>
+           <question>
+               <para>Where can I find more about OpenSER?</para>
+           </question>
+           <answer>
+               <para>
+                       Take a look at &serhomelink;.
+               </para>
+           </answer>
+       </qandaentry>
+       <qandaentry>
+           <question>
+               <para>Where can I post a question about this module?</para>
+           </question>
+           <answer>
+               <para>
+                       First at all check if your question was already answered on one of
+                       our mailing lists: 
+               </para>
+               <itemizedlist>
+                   <listitem>
+                       <para>User Mailing List - &seruserslink;</para>
+                   </listitem>
+                   <listitem>
+                       <para>Developer Mailing List - &serdevlink;</para>
+                   </listitem>
+               </itemizedlist>
+               <para>
+                       E-mails regarding any stable &ser; release should be sent to 
+                       &serusersmail; and e-mails regarding development versions
+                       should be sent to &serdevmail;.
+               </para>
+               <para>
+                       If you want to keep the mail private, send it to 
+                       &serhelpmail;.
+               </para>
+           </answer>
+       </qandaentry>
+       <qandaentry>
+           <question>
+               <para>How can I report a bug?</para>
+           </question>
+           <answer>
+               <para>
+                       Please follow the guidelines provided at:
+                       &serbugslink;.
+               </para>
+           </answer>
+       </qandaentry>
+    </qandaset>
+</chapter>
+
+<!-- Keep this element at the end of the file
+Local Variables:
+sgml-parent-document: ("mysql.sgml" "Book" "chapter")
+End:
+-->
diff --git a/modules/db_mysql/km_doc/mysql_parser.dia b/modules/db_mysql/km_doc/mysql_parser.dia
new file mode 100644 (file)
index 0000000..e17c922
Binary files /dev/null and b/modules/db_mysql/km_doc/mysql_parser.dia differ
diff --git a/modules/db_mysql/km_doc/mysql_user.sgml b/modules/db_mysql/km_doc/mysql_user.sgml
new file mode 100644 (file)
index 0000000..7a41ee9
--- /dev/null
@@ -0,0 +1,156 @@
+<!-- Module User's Guide -->
+
+<chapter>
+       <chapterinfo>
+       <revhistory>
+               <revision>
+               <revnumber>$Revision$</revnumber>
+               <date>$Date$</date>
+               </revision>
+       </revhistory>
+       </chapterinfo>
+       <title>User's Guide</title>
+       
+       <section>
+       <title>Overview</title>
+       <para>
+               This is a module which provides MySQL connectivity for OpenSER.
+               It implements the DB API defined in OpenSER.
+       </para>
+       </section>
+
+       <section>
+       <title>Dependencies</title>
+       <section>
+               <title>&ser; Modules</title>
+               <para>
+               The following modules must be loaded before this module:
+                       <itemizedlist>
+                       <listitem>
+                       <para>
+                               <emphasis>No dependencies on other &ser; modules</emphasis>.
+                       </para>
+                       </listitem>
+                       </itemizedlist>
+               </para>
+       </section>
+       <section>
+               <title>External Libraries or Applications</title>
+               <para>
+               The following libraries or applications must be installed before running
+               &ser; with this module loaded:
+                       <itemizedlist>
+                       <listitem>
+                       <para>
+                               <emphasis>libmysqlclient-dev</emphasis> - the development libraries of mysql-client.
+                       </para>
+                       </listitem>
+                       </itemizedlist>
+               </para>
+       </section>
+       </section>
+       <section>
+       <title>Exported Parameters</title>
+       <section>
+               <title><varname>ping_interval</varname> (integer)</title>
+               <para>
+               Time interval to send ping messages to MySQL server in order to keep
+               the connection open.
+               </para>
+               <para>
+               <emphasis>
+                       Default value is 300 (5 min).
+               </emphasis>
+               </para>
+               <example>
+               <title>Set <varname>ping_interval</varname> parameter</title>
+               <programlisting format="linespecific">
+...
+modparam("mysql", "ping_interval", 600)
+...
+</programlisting>
+               </example>
+       </section>
+               <section>
+               <title><varname>timeout_interval</varname> (integer)</title>
+               <para>
+               Time interval after that an connection attempt, read or write request
+               is aborted. The value counts three times, as several retries are done
+               from the driver before it gives up.
+               </para>
+               <para>
+               The read timeout parameter is ignored on driver versions prior to
+               <quote>5.1.12</quote>, <quote>5.0.25</quote> and <quote>4.1.22</quote>.
+               The write timeout parameter is ignored on version prior to <quote>5.1.12</quote>
+               and <quote>5.0.25</quote>, the <quote>4.1</quote> release don't support it at all.
+               </para>
+               <para>
+               <emphasis>
+                       Default value is 2 (6 sec).
+               </emphasis>
+               </para>
+               <example>
+               <title>Set <varname>timeout_interval</varname> parameter</title>
+               <programlisting format="linespecific">
+...
+modparam("mysql", "timeout_interval", 2)
+...
+</programlisting>
+               </example>
+       </section>
+       <section>
+               <title><varname>auto_reconnect</varname> (integer)</title>
+               <para>
+               Configure the module to auto reconnect to MySQL server if the
+               connection was lost.
+               </para>
+               <para>
+               <emphasis>
+                       Default value is 1 (1 - on / 0 - off).
+               </emphasis>
+               </para>
+               <example>
+               <title>Set <varname>auto_reconnect</varname> parameter</title>
+               <programlisting format="linespecific">
+...
+modparam("auto_reconnect", "auto_reconnect", 0)
+...
+</programlisting>
+               </example>
+       </section>
+       </section>
+       <section>
+       <title>Exported Functions</title>
+               <para>
+               No function exported to be used from configuration file.
+               </para>
+       </section>
+       <section>
+       <title>Installation</title>
+               <para>
+               Because it dependes on an external library, the mysql module is not
+               compiled and installed by default. You can use one of the next options.
+               </para>
+               <itemizedlist>
+                       <listitem>
+                       <para>
+                       - edit the "Makefile" and remove "mysql" from "excluded_modules"
+                       list. Then follow the standard procedure to install &ser;:
+                       "make all; make install".
+                       </para>
+                       </listitem>
+                       <listitem>
+                       <para>
+                       - from command line use: 'make all include_modules="mysql";
+                       make install include_modules="mysql"'.
+                       </para>
+                       </listitem>
+               </itemizedlist>
+       </section>
+</chapter>
+
+<!-- Keep this element at the end of the file
+Local Variables:
+sgml-parent-document: "mysql.sgml" "Book" "chapter")
+End:
+-->
diff --git a/modules/db_mysql/km_my_con.c b/modules/db_mysql/km_my_con.c
new file mode 100644 (file)
index 0000000..e82a827
--- /dev/null
@@ -0,0 +1,124 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2001-2004 iptel.org
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "my_con.h"
+#include "db_mod.h"
+#include <mysql/mysql_version.h>
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+
+
+/*
+ * Create a new connection structure,
+ * open the MySQL connection and set reference count to 1
+ */
+struct my_con* db_mysql_new_connection(const struct db_id* id)
+{
+       struct my_con* ptr;
+
+       if (!id) {
+               LM_ERR("invalid parameter value\n");
+               return 0;
+       }
+
+       ptr = (struct my_con*)pkg_malloc(sizeof(struct my_con));
+       if (!ptr) {
+               LM_ERR("no private memory left\n");
+               return 0;
+       }
+
+       memset(ptr, 0, sizeof(struct my_con));
+       ptr->ref = 1;
+       
+       ptr->con = (MYSQL*)pkg_malloc(sizeof(MYSQL));
+       if (!ptr->con) {
+               LM_ERR("no private memory left\n");
+               goto err;
+       }
+
+       mysql_init(ptr->con);
+
+       if (id->port) {
+               LM_DBG("opening connection: mysql://xxxx:xxxx@%s:%d/%s\n", ZSW(id->host),
+                       id->port, ZSW(id->database));
+       } else {
+               LM_DBG("opening connection: mysql://xxxx:xxxx@%s/%s\n", ZSW(id->host),
+                       ZSW(id->database));
+       }
+
+       // set connect, read and write timeout, the value counts three times
+       mysql_options(ptr->con, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&db_mysql_timeout_interval);
+       mysql_options(ptr->con, MYSQL_OPT_READ_TIMEOUT, (const char *)&db_mysql_timeout_interval);
+       mysql_options(ptr->con, MYSQL_OPT_WRITE_TIMEOUT, (const char *)&db_mysql_timeout_interval);
+
+#if (MYSQL_VERSION_ID >= 40100)
+       if (!mysql_real_connect(ptr->con, id->host, id->username, id->password,
+                               id->database, id->port, 0, CLIENT_MULTI_STATEMENTS)) {
+#else
+       if (!mysql_real_connect(ptr->con, id->host, id->username, id->password,
+                               id->database, id->port, 0, 0)) {
+#endif
+               LM_ERR("driver error: %s\n", mysql_error(ptr->con));
+               mysql_close(ptr->con);
+               goto err;
+       }
+       /* force reconnection if enabled */
+       if (db_mysql_auto_reconnect)
+               ptr->con->reconnect = 1;
+       else 
+               ptr->con->reconnect = 0;
+
+       LM_DBG("connection type is %s\n", mysql_get_host_info(ptr->con));
+       LM_DBG("protocol version is %d\n", mysql_get_proto_info(ptr->con));
+       LM_DBG("server version is %s\n", mysql_get_server_info(ptr->con));
+
+       ptr->timestamp = time(0);
+       ptr->id = (struct db_id*)id;
+       return ptr;
+
+ err:
+       if (ptr && ptr->con) pkg_free(ptr->con);
+       if (ptr) pkg_free(ptr);
+       return 0;
+}
+
+
+/*
+ * Close the connection and release memory
+ */
+void db_mysql_free_connection(struct pool_con* con)
+{
+       if (!con) return;
+
+       struct my_con * _c;
+       _c = (struct my_con*) con;
+
+       if (_c->res) mysql_free_result(_c->res);
+       if (_c->id) free_db_id(_c->id);
+       if (_c->con) {
+               mysql_close(_c->con);
+               pkg_free(_c->con);
+       }
+       pkg_free(_c);
+}
diff --git a/modules/db_mysql/km_my_con.h b/modules/db_mysql/km_my_con.h
new file mode 100644 (file)
index 0000000..455c297
--- /dev/null
@@ -0,0 +1,67 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef MY_CON_H
+#define MY_CON_H
+
+#include "../../db/db_pool.h"
+#include "../../db/db_id.h"
+
+#include <time.h>
+#include <mysql/mysql.h>
+
+
+struct my_con {
+       struct db_id* id;        /* Connection identifier */
+       unsigned int ref;        /* Reference count */
+       struct pool_con* next;   /* Next connection in the pool */
+
+       MYSQL_RES* res;          /* Actual result */
+       MYSQL* con;              /* Connection representation */
+       MYSQL_ROW row;           /* Actual row in the result */
+       time_t timestamp;        /* Timestamp of last query */
+};
+
+
+/*
+ * 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)
+
+
+/*
+ * Create a new connection structure,
+ * open the MySQL connection and set reference count to 1
+ */
+struct my_con* db_mysql_new_connection(const struct db_id* id);
+
+
+/*
+ * Close the connection and release memory
+ */
+void db_mysql_free_connection(struct pool_con* con);
+
+#endif /* MY_CON_H */
diff --git a/modules/db_mysql/km_res.c b/modules/db_mysql/km_res.c
new file mode 100644 (file)
index 0000000..3243919
--- /dev/null
@@ -0,0 +1,196 @@
+/* 
+ * $Id$ 
+ *
+ * MySQL module result related functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <string.h>
+#include <mysql/mysql.h>
+#include "../../db/db_res.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "row.h"
+#include "my_con.h"
+#include "res.h"
+
+
+/*
+ * Get and convert columns from a result
+ */
+int db_mysql_get_columns(const db_con_t* _h, db_res_t* _r)
+{
+       int col;
+       MYSQL_FIELD* fields;
+
+       if ((!_h) || (!_r)) {
+               LM_ERR("invalid parameter\n");
+               return -1;
+       }
+
+       RES_COL_N(_r) = mysql_field_count(CON_CONNECTION(_h));
+       if (!RES_COL_N(_r)) {
+               LM_ERR("no columns returned from the query\n");
+               return -2;
+       } else {
+               LM_DBG("%d columns returned from the query\n", RES_COL_N(_r));
+       }
+       
+       if (db_allocate_columns(_r, RES_COL_N(_r)) != 0) {
+               LM_ERR("could not allocate columns");
+               return -3;
+       }
+
+       fields = mysql_fetch_fields(CON_RESULT(_h));
+       for(col = 0; col < RES_COL_N(_r); col++) {
+               RES_NAMES(_r)[col] = (str*)pkg_malloc(sizeof(str));
+               if (! RES_NAMES(_r)[col]) {
+                       LM_ERR("no private memory left\n");
+                       db_free_columns(_r);
+                       return -4;
+               }
+               LM_DBG("allocate %d bytes for RES_NAMES[%d] at %p\n", sizeof(str), col,
+                               RES_NAMES(_r)[col]);
+
+               /* The pointer that is here returned is part of the result structure. */
+               RES_NAMES(_r)[col]->s = fields[col].name;
+               RES_NAMES(_r)[col]->len = strlen(fields[col].name);
+
+               LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_r)[col], col,
+                               RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s);
+
+               switch(fields[col].type) {
+                       case MYSQL_TYPE_TINY:
+                       case MYSQL_TYPE_SHORT:
+                       case MYSQL_TYPE_LONG:
+                       case MYSQL_TYPE_INT24:
+                       case MYSQL_TYPE_LONGLONG:
+                       case MYSQL_TYPE_DECIMAL:
+                       case MYSQL_TYPE_NEWDECIMAL:
+                       case MYSQL_TYPE_TIMESTAMP:
+                               RES_TYPES(_r)[col] = DB_INT;
+                               break;
+
+                       case MYSQL_TYPE_FLOAT:
+                       case MYSQL_TYPE_DOUBLE:
+                               RES_TYPES(_r)[col] = DB_DOUBLE;
+                               break;
+
+                       case MYSQL_TYPE_DATETIME:
+                               RES_TYPES(_r)[col] = DB_DATETIME;
+                               break;
+
+                       case MYSQL_TYPE_BLOB:
+                               RES_TYPES(_r)[col] = DB_BLOB;
+                               break;
+
+                       case FIELD_TYPE_SET:
+                               RES_TYPES(_r)[col] = DB_BITMAP;
+                               break;
+
+                       case MYSQL_TYPE_STRING:
+                       case MYSQL_TYPE_VAR_STRING:
+                               RES_TYPES(_r)[col] = DB_STRING;
+                               break;
+
+                       default:
+                               LM_WARN("unhandled data type column (%.*s) type id (%d), "
+                                               "use STRING as default\n", RES_NAMES(_r)[col]->len,
+                                               RES_NAMES(_r)[col]->s, fields[col].type);
+                               RES_TYPES(_r)[col] = DB_STRING;
+                               break;
+               }
+       }
+       return 0;
+}
+
+
+/*
+ * Convert rows from mysql to db API representation
+ */
+static inline int db_mysql_convert_rows(const db_con_t* _h, db_res_t* _r)
+{
+       int row, len;
+
+       if ((!_h) || (!_r)) {
+               LM_ERR("invalid parameter\n");
+               return -1;
+       }
+
+       RES_ROW_N(_r) = mysql_num_rows(CON_RESULT(_h));
+       if (!RES_ROW_N(_r)) {
+               RES_ROWS(_r) = 0;
+               return 0;
+       }
+
+       len = sizeof(db_row_t) * RES_ROW_N(_r);
+       RES_ROWS(_r) = (struct db_row*)pkg_malloc(len);
+       if (!RES_ROWS(_r)) {
+               LM_ERR("no private memory left\n");
+               return -2;
+       }
+       LM_DBG("allocate %d bytes for rows at %p", len,
+                       RES_ROWS(_r));
+       memset(RES_ROWS(_r), 0, len);
+
+       for(row = 0; row < RES_ROW_N(_r); row++) {
+               CON_ROW(_h) = mysql_fetch_row(CON_RESULT(_h));
+               if (!CON_ROW(_h)) {
+                       LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h)));
+                       RES_ROW_N(_r) = row;
+                       db_free_rows(_r);
+                       return -3;
+               }
+               if (db_mysql_convert_row(_h, _r, &(RES_ROWS(_r)[row])) < 0) {
+                       LM_ERR("error while converting row #%d\n", row);
+                       RES_ROW_N(_r) = row;
+                       db_free_rows(_r);
+                       return -4;
+               }
+       }
+       return 0;
+}
+
+
+/*
+ * Fill the structure with data from database
+ */
+int db_mysql_convert_result(const db_con_t* _h, db_res_t* _r)
+{
+       if ((!_h) || (!_r)) {
+               LM_ERR("invalid parameter\n");
+               return -1;
+       }
+
+       if (db_mysql_get_columns(_h, _r) < 0) {
+               LM_ERR("error while getting column names\n");
+               return -2;
+       }
+
+       if (db_mysql_convert_rows(_h, _r) < 0) {
+               LM_ERR("error while converting rows\n");
+               db_free_columns(_r);
+               return -3;
+       }
+       return 0;
+}
+
diff --git a/modules/db_mysql/km_res.h b/modules/db_mysql/km_res.h
new file mode 100644 (file)
index 0000000..7c693cf
--- /dev/null
@@ -0,0 +1,40 @@
+/* 
+ * $Id$ 
+ *
+ * MySQL module result related functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef RES_H
+#define RES_H
+
+#include "../../db/db_res.h"
+#include "../../db/db_con.h"
+
+
+/*
+ * Fill the structure with data from database
+ */
+int db_mysql_convert_result(const db_con_t* _h, db_res_t* _r);
+
+int db_mysql_get_columns(const db_con_t* _h, db_res_t* _r);
+
+#endif /* RES_H */
diff --git a/modules/db_mysql/km_row.c b/modules/db_mysql/km_row.c
new file mode 100644 (file)
index 0000000..f6a07c8
--- /dev/null
@@ -0,0 +1,74 @@
+/* 
+ * $Id$ 
+ *
+ * MySQL module row related functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "../../db/db_row.h"
+#include "../../db/db_ut.h"
+#include "my_con.h"
+#include "val.h"
+#include "row.h"
+
+
+/*
+ * Convert a row from result into db API representation
+ */
+int db_mysql_convert_row(const db_con_t* _h, db_res_t* _res, db_row_t* _r)
+{
+       unsigned long* lengths;
+       int i, len;
+
+       if ((!_h) || (!_res) || (!_r)) {
+               LM_ERR("invalid parameter value\n");
+               return -1;
+       }
+
+       len = sizeof(db_val_t) * RES_COL_N(_res);
+       ROW_VALUES(_r) = (db_val_t*)pkg_malloc(len);
+       if (!ROW_VALUES(_r)) {
+               LM_ERR("no private memory left\n");
+               return -1;
+       }
+       LM_DBG("allocate %d bytes for row values at %p", len,
+                       ROW_VALUES(_r));
+
+       memset(ROW_VALUES(_r), 0, len);
+       /* Save the number of columns in the ROW structure */
+       ROW_N(_r) = RES_COL_N(_res);
+
+       lengths = mysql_fetch_lengths(CON_RESULT(_h));
+
+       for(i = 0; i < RES_COL_N(_res); i++) {
+               if (db_mysql_str2val(RES_TYPES(_res)[i], &(ROW_VALUES(_r)[i]),
+                           ((MYSQL_ROW)CON_ROW(_h))[i], lengths[i]) < 0) {
+                       LM_ERR("failed to convert value\n");
+                       LM_DBG("free row at %pn", _r);
+                       db_free_row(_r);
+                       return -3;
+               }
+       }
+       return 0;
+}
diff --git a/modules/db_mysql/km_row.h b/modules/db_mysql/km_row.h
new file mode 100644 (file)
index 0000000..b22337b
--- /dev/null
@@ -0,0 +1,39 @@
+/* 
+ * $Id$ 
+ *
+ * MySQL module row related functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef ROW_H
+#define ROW_H
+
+#include "../../db/db_con.h"
+#include "../../db/db_res.h"
+#include "../../db/db_row.h"
+
+
+/*
+ * Convert a row from result into db API representation
+ */
+int db_mysql_convert_row(const db_con_t* _h, db_res_t* _res, db_row_t* _r);
+
+#endif /* ROW_H */
diff --git a/modules/db_mysql/km_val.c b/modules/db_mysql/km_val.c
new file mode 100644 (file)
index 0000000..55fc220
--- /dev/null
@@ -0,0 +1,242 @@
+/* 
+ * $Id$ 
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "../../dprint.h"
+#include "../../db/db_ut.h"
+#include "val.h"
+#include "my_con.h"
+
+#include <string.h>
+#include <stdio.h>
+
+
+/*
+ * Convert str to db value, does not copy strings
+ */
+int db_mysql_str2val(const db_type_t _t, db_val_t* _v, const char* _s, const int _l)
+{
+       static str dummy_string = {"", 0};
+       
+       if (!_v) {
+               LM_ERR("invalid parameter value\n");
+               return -1;
+       }
+
+       if (!_s) {
+               memset(_v, 0, sizeof(db_val_t));
+                       /* Initialize the string pointers to a dummy empty
+                        * string so that we do not crash when the NULL flag
+                        * is set but the module does not check it properly
+                        */
+               VAL_STRING(_v) = dummy_string.s;
+               VAL_STR(_v) = dummy_string;
+               VAL_BLOB(_v) = dummy_string;
+               VAL_TYPE(_v) = _t;
+               VAL_NULL(_v) = 1;
+               return 0;
+       }
+       VAL_NULL(_v) = 0;
+
+       switch(_t) {
+       case DB_INT:
+               LM_DBG("converting INT [%s]\n", _s);
+               if (db_str2int(_s, &VAL_INT(_v)) < 0) {
+                       LM_ERR("error while converting integer value from string\n");
+                       return -2;
+               } else {
+                       VAL_TYPE(_v) = DB_INT;
+                       return 0;
+               }
+               break;
+
+       case DB_BITMAP:
+               LM_DBG("converting BITMAP [%s]\n", _s);
+               if (db_str2int(_s, &VAL_INT(_v)) < 0) {
+                       LM_ERR("error while converting bitmap value from string\n");
+                       return -3;
+               } else {
+                       VAL_TYPE(_v) = DB_BITMAP;
+                       return 0;
+               }
+               break;
+       
+       case DB_DOUBLE:
+               LM_DBG("converting DOUBLE [%s]\n", _s);
+               if (db_str2double(_s, &VAL_DOUBLE(_v)) < 0) {
+                       LM_ERR("error while converting double value from string\n");
+                       return -4;
+               } else {
+                       VAL_TYPE(_v) = DB_DOUBLE;
+                       return 0;
+               }
+               break;
+
+       case DB_STRING:
+               LM_DBG("converting STRING [%s]\n", _s);
+               VAL_STRING(_v) = _s;
+               VAL_TYPE(_v) = DB_STRING;
+               return 0;
+
+       case DB_STR:
+               LM_DBG("converting STR [%.*s]\n", _l, _s);
+               VAL_STR(_v).s = (char*)_s;
+               VAL_STR(_v).len = _l;
+               VAL_TYPE(_v) = DB_STR;
+               return 0;
+
+       case DB_DATETIME:
+               LM_DBG("converting DATETIME [%s]\n", _s);
+               if (db_str2time(_s, &VAL_TIME(_v)) < 0) {
+                       LM_ERR("error while converting datetime value from string\n");
+                       return -5;
+               } else {
+                       VAL_TYPE(_v) = DB_DATETIME;
+                       return 0;
+               }
+               break;
+
+       case DB_BLOB:
+               LM_DBG("converting BLOB [%.*s]\n", _l, _s);
+               VAL_BLOB(_v).s = (char*)_s;
+               VAL_BLOB(_v).len = _l;
+               VAL_TYPE(_v) = DB_BLOB;
+               return 0;
+       }
+       return -6;
+}
+
+
+/*
+ * Used when converting result from a query
+ */
+int db_mysql_val2str(const db_con_t* _c, const db_val_t* _v, char* _s, int* _len)
+{
+       int l;
+       char* old_s;
+
+       if (!_c || !_v || !_s || !_len || !*_len) {
+               LM_ERR("invalid parameter value\n");
+               return -1;
+       }
+
+       if (VAL_NULL(_v)) {
+               if (*_len < sizeof("NULL")) {
+                       LM_ERR("buffer too small\n");
+                       return -1;
+               }
+               *_len = snprintf(_s, *_len, "NULL");
+               return 0;
+       }
+       
+       switch(VAL_TYPE(_v)) {
+       case DB_INT:
+               if (db_int2str(VAL_INT(_v), _s, _len) < 0) {
+                       LM_ERR("error while converting string to int\n");
+                       return -2;
+               } else {
+                       return 0;
+               }
+               break;
+
+       case DB_BITMAP:
+               if (db_int2str(VAL_BITMAP(_v), _s, _len) < 0) {
+                       LM_ERR("error while converting string to int\n");
+                       return -3;
+               } else {
+                       return 0;
+               }
+               break;
+
+       case DB_DOUBLE:
+               if (db_double2str(VAL_DOUBLE(_v), _s, _len) < 0) {
+                       LM_ERR("error while converting string to double\n");
+                       return -4;
+               } else {
+                       return 0;
+               }
+               break;
+
+       case DB_STRING:
+               l = strlen(VAL_STRING(_v));
+               if (*_len < (l * 2 + 3)) {
+                       LM_ERR("destination buffer too short\n");
+                       return -5;
+               } else {
+                       old_s = _s;
+                       *_s++ = '\'';
+                       _s += mysql_real_escape_string(CON_CONNECTION(_c), _s, VAL_STRING(_v), l);
+                       *_s++ = '\'';
+                       *_s = '\0'; /* FIXME */
+                       *_len = _s - old_s;
+                       return 0;
+               }
+               break;
+
+       case DB_STR:
+               l = VAL_STR(_v).len;
+               if (*_len < (l * 2 + 3)) {
+                       LM_ERR("destination buffer too short\n");
+                       return -6;
+               } else {
+                       old_s = _s;
+                       *_s++ = '\'';
+                       _s += mysql_real_escape_string(CON_CONNECTION(_c), _s, VAL_STR(_v).s, l);
+                       *_s++ = '\'';
+                       *_s = '\0';
+                       *_len = _s - old_s;
+                       return 0;
+               }
+               break;
+
+       case DB_DATETIME:
+               if (db_time2str(VAL_TIME(_v), _s, _len) < 0) {
+                       LM_ERR("error while converting string to time_t\n");
+                       return -7;
+               } else {
+                       return 0;
+               }
+               break;
+
+       case DB_BLOB:
+               l = VAL_BLOB(_v).len;
+               if (*_len < (l * 2 + 3)) {
+                       LM_ERR("destination buffer too short\n");
+                       return -8;
+               } else {
+                       old_s = _s;
+                       *_s++ = '\'';
+                       _s += mysql_real_escape_string(CON_CONNECTION(_c), _s, VAL_STR(_v).s, l);
+                       *_s++ = '\'';
+                       *_s = '\0';
+                       *_len = _s - old_s;
+                       return 0;
+               }                       
+               break;
+
+       default:
+               LM_DBG("unknown data type\n");
+               return -9;
+       }
+       /*return -8; --not reached*/
+}
diff --git a/modules/db_mysql/km_val.h b/modules/db_mysql/km_val.h
new file mode 100644 (file)
index 0000000..2edd8fc
--- /dev/null
@@ -0,0 +1,43 @@
+/* 
+ * $Id$ 
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef VAL_H
+#define VAL_H
+
+#include <mysql/mysql.h>
+#include "../../db/db_val.h"
+#include "../../db/db.h"
+
+
+/*
+ * Does not copy strings
+ */
+int db_mysql_str2val(const db_type_t _t, db_val_t* _v, const char* _s, const int _l);
+
+
+/*
+ * Used when converting result from a query
+ */
+int db_mysql_val2str(const db_con_t* _con, const db_val_t* _v, char* _s, int* _len);
+
+#endif /* VAL_H */