瀏覽代碼

MySQL connection pool. Connections with the same URL are shared among
modules within the same process.

Jan Janak 21 年之前
父節點
當前提交
cf63aa85e1

+ 12 - 104
modules/db_mysql/dbase.c

@@ -35,7 +35,8 @@
 #include "../../dprint.h"
 #include "utils.h"
 #include "val.h"
-#include "con_mysql.h"
+#include "my_con.h"
+#include "my_pool.h"
 #include "res.h"
 #include "dbase.h"
 
@@ -45,95 +46,6 @@
 static char sql_buf[SQL_BUF_LEN];
 
 
-/*
- * Establish a database connection,
- * returns 1 on success, 0 otherwise
- * _h is a handle used in communication with database
- *
- * URL is in form mysql://user:password@host:port/database
- */
-static int connect_db(db_con_t* _h, const char* _db_url)
-{
-	int p, l, res;
-	char* user, *password, *host, *port, *database;
-	char* buf;
-
-	if ((!_h) || (!_db_url)) {
-		LOG(L_ERR, "connect_db(): Invalid parameter value\n");
-		return -1;
-	}
-
-	CON_CONNECTED(_h) = 0;
-
-	     /* Make a scratch pad copy of given SQL URL */
-	l = strlen(_db_url);
-	buf = (char*)pkg_malloc(l + 1);
-	if (!buf) {
-		LOG(L_ERR, "connect_db(): Not enough memory\n");
-		return -2;
-	}
-	memcpy(buf, _db_url, l + 1);
-
-	res = parse_mysql_url(buf, &user, &password, &host, &port, &database);
-	if (port && *port) {
-		p = atoi(port);
-	} else {
-		p = 0;
-	}
-	
-	if (res < 0) {
-		LOG(L_ERR, "connect_db(): Error while parsing SQL URL\n");
-		pkg_free(buf);
-		return -3;
-	}
-
-	CON_CONNECTION(_h) = (MYSQL*)pkg_malloc(sizeof(MYSQL));
-	if (!CON_CONNECTION(_h)) {
-		LOG(L_ERR, "connect_db(): No enough memory\n");
-		pkg_free(buf);
-		return -4;
-	}
-
-	mysql_init(CON_CONNECTION(_h));
-
-	if (!mysql_real_connect(CON_CONNECTION(_h), host, user, password, database, p, 0, 0)) {
-		LOG(L_ERR, "connect_db(): %s\n", mysql_error(CON_CONNECTION(_h)));
-		mysql_close(CON_CONNECTION(_h));
-		pkg_free(buf);
-		pkg_free(CON_CONNECTION(_h));
-		return -5;
-	}
-
-	pkg_free(buf);
-	CON_CONNECTED(_h) = 1;
-	return 0;
-}
-
-
-/*
- * Disconnect database connection
- *
- * disconnects database connection represented by _handle
- * returns 1 on success, 0 otherwise
- */
-static int disconnect_db(db_con_t* _h)
-{
-	if (!_h) {
-		LOG(L_ERR, "disconnect_db(): Invalid parameter value\n");
-		return -1;
-	}
-
-	if (CON_CONNECTED(_h) == 1) {
-		mysql_close(CON_CONNECTION(_h));
-		CON_CONNECTED(_h) = 0;
-		pkg_free(CON_CONNECTION(_h));
-		return 0;
-	} else {
-		return -2;
-	}
-}
-
-
 /*
  * Send an SQL query to the server
  */
@@ -272,25 +184,26 @@ static int print_set(char* _b, int _l, db_key_t* _k, db_val_t* _v, int _n)
  * Initialize database module
  * No function should be called before this
  */
-db_con_t* db_init(const char* _sqlurl)
+db_con_t* db_init(const char* _url)
 {
 	db_con_t* res;
 
-	if (!_sqlurl) {
+	if (!_url) {
 		LOG(L_ERR, "db_init(): Invalid parameter value\n");
 		return 0;
 	}
 
-	res = pkg_malloc(sizeof(db_con_t) + sizeof(struct con_mysql));
+	res = pkg_malloc(sizeof(db_con_t) + sizeof(struct my_con*));
 	if (!res) {
 		LOG(L_ERR, "db_init(): No memory left\n");
 		return 0;
-	} else {
-		memset(res, 0, sizeof(db_con_t) + sizeof(struct con_mysql));
 	}
+	memset(res, 0, sizeof(db_con_t) + sizeof(struct my_con*));
+
+	(struct my_con*)res->tail = get_connection(_url);
 
-	if (connect_db(res, _sqlurl) < 0) {
-		LOG(L_ERR, "db_init(): Error while trying to connect database\n");
+	if (!res->tail) {
+		LOG(L_ERR, "db_init(): Could not create a connection\n");
 		pkg_free(res);
 		return 0;
 	}
@@ -310,13 +223,8 @@ void db_close(db_con_t* _h)
 		return;
 	}
 
-	disconnect_db(_h);
-	if (CON_RESULT(_h)) {
-		mysql_free_result(CON_RESULT(_h));
-	}
-	if (CON_TABLE(_h)) {
-		pkg_free(CON_TABLE(_h));
-	}
+	release_connection((struct my_con*)_h->tail);
+	if (CON_TABLE(_h)) pkg_free(CON_TABLE(_h));
 	pkg_free(_h);
 }
 

+ 95 - 0
modules/db_mysql/my_con.c

@@ -0,0 +1,95 @@
+/* 
+ * $Id$
+ *
+ *
+ * Copyright (C) 2001-2004 iptel.org
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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 "my_con.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "utils.h"
+
+
+/*
+ * Create a new connection structure,
+ * open the MySQL connection and set reference count to 1
+ */
+struct my_con* new_connection(struct my_id* id)
+{
+	struct my_con* ptr;
+
+	if (!id) {
+		LOG(L_ERR, "new_connection(): Invalid parameter value\n");
+		return 0;
+	}
+
+	ptr = (struct my_con*)pkg_malloc(sizeof(struct my_con));
+	if (!ptr) {
+		LOG(L_ERR, "new_connection(): No 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) {
+		LOG(L_ERR, "new_connection(): No enough memory\n");
+		goto err;
+	}
+
+	mysql_init(ptr->con);
+
+	if (!mysql_real_connect(ptr->con, id->host.s, id->username.s, id->password.s, id->database.s, id->port, 0, 0)) {
+		LOG(L_ERR, "new_connection(): %s\n", mysql_error(ptr->con));
+		mysql_close(ptr->con);
+		goto err;
+	}
+
+	ptr->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 free_connection(struct my_con* con)
+{
+	if (!con) return;
+	if (con->res) mysql_free_result(con->res);
+	if (con->id) free_my_id(con->id);
+	if (con->con) {
+		mysql_close(con->con);
+		pkg_free(con->con);
+	}
+	pkg_free(con);
+}

+ 65 - 0
modules/db_mysql/my_con.h

@@ -0,0 +1,65 @@
+/* 
+ * $Id$
+ *
+ *
+ * Copyright (C) 2001-2003 Fhg Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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 <mysql/mysql.h>
+#include "my_id.h"
+
+struct my_con {
+	struct my_id* id;     /* Connection identifier */
+	int ref;             /* Reference count */
+	MYSQL_RES* res;      /* Actual result */
+	MYSQL* con;          /* Connection representation */
+	MYSQL_ROW row;       /* Actual row in the result */
+	struct my_con* next; /* Next connection in the pool */
+};
+
+
+/*
+ * 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)
+
+
+/*
+ * Create a new connection structure,
+ * open the MySQL connection and set reference count to 1
+ */
+struct my_con* new_connection(struct my_id* id);
+
+
+/*
+ * Close the connection and release memory
+ */
+void free_connection(struct my_con* con);
+
+#endif /* MY_CON_H */

+ 156 - 0
modules/db_mysql/my_id.c

@@ -0,0 +1,156 @@
+/* 
+ * $Id$
+ *
+ *
+ * Copyright (C) 2001-2004 iptel.org
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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 <stdlib.h>
+#include <string.h>
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "my_id.h"
+#include "utils.h"
+
+
+/*
+ * Create a new connection identifier
+ */
+struct my_id* new_my_id(const char* url)
+{
+	char* buf, *username, *password, *host, *port, *database;
+	int l;
+	struct my_id* ptr;
+
+	if (!url) {
+		LOG(L_ERR, "new_my_id(): Invalid parameter\n");
+		return 0;
+	}
+
+	     /* Make a scratch-pad copy of the url */
+	l = strlen(url);
+	buf = (char*)pkg_malloc(l + 1);
+	if (!buf) {
+		LOG(L_ERR, "new_my_id(): Not enough memory\n");
+		return 0;
+	}
+	memcpy(buf, url, l + 1);
+
+	ptr = (struct my_id*)pkg_malloc(sizeof(struct my_id));
+	if (!ptr) {
+		LOG(L_ERR, "new_my_id(): No memory left\n");
+		goto err;
+	}
+	memset(ptr, 0, sizeof(struct my_id));
+
+	if (parse_mysql_url(buf, &username, &password, &host, &port, &database) < 0) {
+		LOG(L_ERR, "new_my_id(): Error while parsing mysql URL: %s\n", url);
+		goto err;
+	}
+
+	ptr->username.len = strlen(username);
+	ptr->username.s = (char*)pkg_malloc(ptr->username.len + 1);
+	if (!ptr->username.s) {
+		LOG(L_ERR, "new_connection(): No memory left\n");
+		goto err;
+	}
+	memcpy(ptr->username.s, username, ptr->username.len + 1);
+
+	ptr->password.len = strlen(password);
+	ptr->password.s = (char*)pkg_malloc(ptr->password.len + 1);
+	if (!ptr->password.s) {
+		LOG(L_ERR, "new_connection(): No memory left\n");
+		goto err;
+	}
+	memcpy(ptr->password.s, password, ptr->password.len + 1);
+
+	ptr->host.len = strlen(host);
+	ptr->host.s = (char*)pkg_malloc(ptr->host.len + 1);
+	if (!ptr->host.s) {
+		LOG(L_ERR, "new_connection(): No memory left\n");
+		goto err;
+	}
+	memcpy(ptr->host.s, host, ptr->host.len + 1);
+
+	if (port && *port) {
+		ptr->port = atoi(port);
+	} else {
+		ptr->port = 0;
+	}
+
+	ptr->database.len = strlen(database);
+	ptr->database.s = (char*)pkg_malloc(ptr->database.len + 1);
+	if (!ptr->database.s) {
+		LOG(L_ERR, "new_connection(): No memory left\n");
+		goto err;
+	}
+	memcpy(ptr->database.s, database, ptr->database.len + 1);
+
+	pkg_free(buf);
+	return ptr;
+
+ err:
+	if (buf) pkg_free(buf);
+	if (ptr && ptr->username.s) pkg_free(ptr->username.s);
+	if (ptr && ptr->password.s) pkg_free(ptr->password.s);
+	if (ptr && ptr->host.s) pkg_free(ptr->host.s);
+	if (ptr && ptr->database.s) pkg_free(ptr->database.s);
+	if (ptr) pkg_free(ptr);
+	return 0;
+}
+
+
+/*
+ * Compare two connection identifiers
+ */
+unsigned char cmp_my_id(struct my_id* id1, struct my_id* id2)
+{
+	if (!id1 || !id2) return 0;
+	if (id1->port != id2->port) return 0;
+	if (id1->username.len != id2->username.len) return 0;
+	if (id1->password.len != id2->password.len) return 0;
+	if (id1->host.len != id2->host.len) return 0;
+	if (id1->database.len != id2->database.len) return 0;
+
+	if (memcmp(id1->username.s, id2->username.s, id1->username.len)) return 0;
+	if (memcmp(id1->password.s, id2->password.s, id1->password.len)) return 0;
+	if (strncasecmp(id1->host.s, id2->host.s, id1->host.len)) return 0;
+	if (memcmp(id1->database.s, id2->database.s, id1->database.len)) return 0;
+	return 1;
+}
+
+
+/*
+ * Free a connection identifier
+ */
+void free_my_id(struct my_id* id)
+{
+	if (!id) return;
+
+	if (id->username.s) pkg_free(id->username.s);
+	if (id->password.s) pkg_free(id->password.s);
+	if (id->host.s) pkg_free(id->host.s);
+	if (id->database.s) pkg_free(id->database.s);
+	pkg_free(id);
+}

+ 65 - 0
modules/db_mysql/my_id.h

@@ -0,0 +1,65 @@
+/* 
+ * $Id$
+ *
+ *
+ * Copyright (C) 2001-2004 iptel.org
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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_ID_H
+#define MY_ID_H
+
+#include "../../str.h"
+
+/*
+ * All str fields must be zero terminated because we
+ * will pass them to mysql_real_connect
+ */
+struct my_id {
+	str username;  /* Username, case sensitive */
+	str password;  /* Password, case sensitive */
+	str host;      /* Host or IP, case insensitive */
+	unsigned short port; /* Port number */
+	str database;  /* Database, case sensitive */
+};
+
+
+/*
+ * Create a new connection identifier
+ */
+struct my_id* new_my_id(const char* url);
+
+
+/*
+ * Compare two connection identifiers
+ */
+unsigned char cmp_my_id(struct my_id* id1, struct my_id* id2);
+
+
+/*
+ * Free a connection identifier
+ */
+void free_my_id(struct my_id* id);
+
+
+#endif /* MY_ID_H */

+ 135 - 0
modules/db_mysql/my_pool.c

@@ -0,0 +1,135 @@
+/* 
+ * $Id$
+ *
+ *
+ * Copyright (C) 2001-2004 iptel.org
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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 <unistd.h>
+#include "../../dprint.h"
+#include "my_pool.h"
+#include "my_id.h"
+
+
+/* The head of the pool */
+static struct my_con* pool = 0;
+
+/*
+ * Pid of the process that added the last
+ * connection to the pool. This is used to
+ * check for inherited database connections.
+ */
+static int pool_pid;
+
+
+/*
+ * Get a connection from the pool, reuse existing
+ * if possible, otherwise create a new one
+ */
+struct my_con* get_connection(const char* url)
+{
+	struct my_id* id;
+	struct my_con* ptr;
+	int pid;
+
+	if (!url) {
+		LOG(L_ERR, "get_connection(): Invalid parameter value\n");
+		return 0;
+	}
+
+	pid = getpid();
+	if (pool && (pool_pid != pid)) {
+		LOG(L_ERR, "get_connection(): Inherited open database connections, this is not a good idea\n");
+		return 0;
+	}
+
+	pool_pid = pid;
+
+	id = new_my_id(url);
+	if (!id) return 0;
+
+	ptr = pool;
+	while (ptr) {
+		if (cmp_my_id(id, ptr->id)) {
+			DBG("get_connection(): Connection found in the pool\n");
+			ptr->ref++;
+			free_my_id(id);
+			return ptr;
+		}
+		ptr = ptr->next;
+	}
+
+	DBG("get_connection(): Connection not found in the pool\n");
+	ptr = new_connection(id);
+	if (!ptr) {
+		free_my_id(id);
+		return 0;
+	}
+
+	ptr->next = pool;
+	pool = ptr;
+	return ptr;
+}
+
+
+/*
+ * Release a connection, the connection will be left
+ * in the pool if ref count != 0, otherwise it
+ * will be delete completely
+ */
+void release_connection(struct my_con* con)
+{
+	struct my_con* ptr;
+
+	if (!con) return;
+
+	if (con->ref > 1) {
+		     /* There are still other users, just
+		      * decrease the reference count and return
+		      */
+		DBG("release_connection(): Connection still kept in the pool\n");
+		con->ref--;
+		return;
+	}
+
+	DBG("release_connection(): Removing connection from the pool\n");
+
+	if (pool == con) {
+		pool = pool->next;
+	} else {
+		ptr = pool;
+		while(ptr) {
+			if (ptr->next == con) break;
+			ptr = ptr->next;
+		}
+		if (!ptr) {
+			LOG(L_ERR, "release_connection(): Weird, connection not found in the pool\n");
+		} else {
+			     /* Remove the connection from the pool */
+			ptr->next = con->next;
+		}
+	}
+
+	free_connection(con);
+}

+ 14 - 15
modules/db_mysql/con_mysql.h → modules/db_mysql/my_pool.h

@@ -2,7 +2,7 @@
  * $Id$
  *
  *
- * Copyright (C) 2001-2003 Fhg Fokus
+ * Copyright (C) 2001-2004 iptel.org
  *
  * This file is part of ser, a free SIP server.
  *
@@ -26,25 +26,24 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#ifndef MY_POOL_H
+#define MY_POOL_H
 
-#ifndef CON_MYSQL_H
-#define CON_MYSQL_H
-
-#include <mysql/mysql.h>
+#include "my_con.h"
 
 /*
- * MySQL specific connection data
+ * Get a connection from the pool, reuse existing
+ * if possible, otherwise create a new one
  */
-struct con_mysql {
-	MYSQL_RES* res; /* Actual result */
-	MYSQL* con;     /* Connection representation */
-	MYSQL_ROW row;  /* Actual row in the result */
-};
+struct my_con* get_connection(const char* url);
 
 
-#define CON_RESULT(db_con)     (((struct con_mysql*)((db_con)->tail))->res)
-#define CON_CONNECTION(db_con) (((struct con_mysql*)((db_con)->tail))->con)
-#define CON_ROW(db_con)        (((struct con_mysql*)((db_con)->tail))->row)
+/*
+ * Release a connection, the connection will be left
+ * in the pool if ref count != 0, otherwise it
+ * will be delete completely
+ */
+void release_connection(struct my_con* con);
 
 
-#endif /* CON_MYSQL_H */
+#endif /* MY_POOL_H */

+ 1 - 1
modules/db_mysql/res.c

@@ -32,7 +32,7 @@
 #include "../../mem/mem.h"
 #include "../../dprint.h"
 #include "row.h"
-#include "con_mysql.h"
+#include "my_con.h"
 #include "res.h"
 
 

+ 1 - 1
modules/db_mysql/row.c

@@ -32,7 +32,7 @@
 #include "../../mem/mem.h"
 #include <mysql/mysql.h>
 #include "val.h"
-#include "con_mysql.h"
+#include "my_con.h"
 #include "row.h"