Sfoglia il codice sorgente

- improved database connection pool

Jan Janak 20 anni fa
parent
commit
18c6ca6603
4 ha cambiato i file con 533 aggiunte e 0 eliminazioni
  1. 274 0
      db/db_id.c
  2. 62 0
      db/db_id.h
  3. 120 0
      db/db_pool.c
  4. 77 0
      db/db_pool.h

+ 274 - 0
db/db_id.c

@@ -0,0 +1,274 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2001-2005 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 "db_id.h"
+#include "../dprint.h"
+#include "../mem/mem.h"
+#include "../ut.h"
+#include <stdlib.h>
+#include <string.h>
+
+
+/*
+ * Duplicate a string
+ */
+static int dupl_string(char** dst, const char* begin, const char* end)
+{
+	if (*dst) pkg_free(*dst);
+
+	*dst = pkg_malloc(end - begin + 1);
+	if ((*dst) == NULL) {
+		return -1;
+	}
+
+	memcpy(*dst, begin, end - begin);
+	(*dst)[end - begin] = '\0';
+	return 0;
+}
+
+
+/*
+ * Parse a database URL of form 
+ * scheme://[username[:password]@]hostname[:port]/database
+ *
+ * Returns 0 if parsing was successful and -1 otherwise
+ */
+static int parse_db_url(struct db_id* id, const char* url)
+{
+#define SHORTEST_DB_URL "s://a/b"
+#define SHORTEST_DB_URL_LEN (sizeof(SHORTEST_DB_URL) - 1)
+
+	enum state {
+		ST_SCHEME,     /* Scheme part */
+		ST_SLASH1,     /* First slash */
+		ST_SLASH2,     /* Second slash */
+		ST_USER_HOST,  /* Username or hostname */
+		ST_PASS_PORT,  /* Password or port part */
+		ST_HOST,       /* Hostname part */
+		ST_PORT,       /* Port part */
+		ST_DB          /* Database part */
+	};
+
+	enum state st;
+	int len, i;
+	const char* begin;
+	char* prev_token;
+
+	prev_token = 0;
+
+	if (!id || !url) {
+		goto err;
+	}
+	
+	len = strlen(url);
+	if (len < SHORTEST_DB_URL_LEN) {
+		goto err;
+	}
+	
+	     /* Initialize all attributes to 0 */
+	memset(id, 0, sizeof(struct db_id));
+	st = ST_SCHEME;
+	begin = url;
+
+	for(i = 0; i < len; i++) {
+		switch(st) {
+		case ST_SCHEME:
+			switch(url[i]) {
+			case ':':
+				st = ST_SLASH1;
+				if (dupl_string(&id->scheme, begin, url + i) < 0) goto err;
+				break;
+			}
+			break;
+
+		case ST_SLASH1:
+			switch(url[i]) {
+			case '/':
+				st = ST_SLASH2;
+				break;
+
+			default:
+				goto err;
+			}
+			break;
+
+		case ST_SLASH2:
+			switch(url[i]) {
+			case '/':
+				st = ST_USER_HOST;
+				begin = url + i + 1;
+				break;
+				
+			default:
+				goto err;
+			}
+			break;
+
+		case ST_USER_HOST:
+			switch(url[i]) {
+			case '@':
+				st = ST_HOST;
+				if (dupl_string(&id->username, begin, url + i) < 0) goto err;
+				begin = url + i + 1;
+				break;
+
+			case ':':
+				st = ST_PASS_PORT;
+				if (dupl_string(&prev_token, begin, url + i) < 0) goto err;
+				begin = url + i + 1;
+				break;
+
+			case '/':
+				if (dupl_string(&id->host, begin, url + i) < 0) goto err;
+				if (dupl_string(&id->database, url + i + 1, url + len) < 0) goto err;
+				return 0;
+			}
+			break;
+
+		case ST_PASS_PORT:
+			switch(url[i]) {
+			case '@':
+				st = ST_HOST;
+				id->username = prev_token;
+				if (dupl_string(&id->password, begin, url + i) < 0) goto err;
+				begin = url + i + 1;
+				break;
+
+			case '/':
+				id->host = prev_token;
+				id->port = str2s(begin, url + i - begin, 0);
+				if (dupl_string(&id->database, url + i + 1, url + len) < 0) goto err;
+				return 0;
+			}
+			break;
+
+		case ST_HOST:
+			switch(url[i]) {
+			case ':':
+				st = ST_PORT;
+				if (dupl_string(&id->host, begin, url + i) < 0) goto err;
+				begin = url + i + 1;
+				break;
+
+			case '/':
+				if (dupl_string(&id->host, begin, url + i) < 0) goto err;
+				if (dupl_string(&id->database, url + i + 1, url + len) < 0) goto err;
+				return 0;
+			}
+			break;
+
+		case ST_PORT:
+			switch(url[i]) {
+			case '/':
+				id->port = str2s(begin, url + i - begin, 0);
+				if (dupl_string(&id->database, url + i + 1, url + len) < 0) goto err;
+				return 0;
+			}
+			break;
+			
+		case ST_DB:
+			break;
+		}
+	}
+
+	if (st != ST_DB) goto err;
+	return 0;
+
+ err:
+	if (id->scheme) pkg_free(id->scheme);
+	if (id->username) pkg_free(id->username);
+	if (id->password) pkg_free(id->password);
+	if (id->host) pkg_free(id->host);
+	if (id->database) pkg_free(id->database);
+	if (prev_token) pkg_free(prev_token);
+	return -1;
+}
+
+
+/*
+ * Create a new connection identifier
+ */
+struct db_id* new_db_id(const char* url)
+{
+	struct db_id* ptr;
+
+	if (!url) {
+		LOG(L_ERR, "new_db_id: Invalid parameter\n");
+		return 0;
+	}
+
+	ptr = (struct db_id*)pkg_malloc(sizeof(struct db_id));
+	if (!ptr) {
+		LOG(L_ERR, "new_db_id: No memory left\n");
+		goto err;
+	}
+	memset(ptr, 0, sizeof(struct db_id));
+
+	if (parse_db_url(ptr, url) < 0) {
+		LOG(L_ERR, "new_db_id: Error while parsing database URL: %s\n", url);
+		goto err;
+	}
+
+	return ptr;
+
+ err:
+	if (ptr) pkg_free(ptr);
+	return 0;
+}
+
+
+/*
+ * Compare two connection identifiers
+ */
+unsigned char cmp_db_id(struct db_id* id1, struct db_id* id2)
+{
+	if (!id1 || !id2) return 0;
+	if (id1->port != id2->port) return 0;
+
+	if (strcmp(id1->scheme, id2->scheme)) return 0;
+	if (strcmp(id1->username, id2->username)) return 0;
+	if (strcmp(id1->password, id2->password)) return 0;
+	if (strcasecmp(id1->host, id2->host)) return 0;
+	if (strcmp(id1->database, id2->database)) return 0;
+	return 1;
+}
+
+
+/*
+ * Free a connection identifier
+ */
+void free_db_id(struct db_id* id)
+{
+	if (!id) return;
+
+	if (id->scheme) pkg_free(id->scheme);
+	if (id->username) pkg_free(id->username);
+	if (id->password) pkg_free(id->password);
+	if (id->host) pkg_free(id->host);
+	if (id->database) pkg_free(id->database);
+	pkg_free(id);
+}

+ 62 - 0
db/db_id.h

@@ -0,0 +1,62 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2001-2005 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 _DB_ID_H
+#define _DB_ID_H
+
+#include "../str.h"
+
+
+struct db_id {
+	char* scheme;        /* URL scheme */
+	char* username;      /* Username, case sensitive */
+	char* password;      /* Password, case sensitive */
+	char* host;          /* Host or IP, case insensitive */
+	unsigned short port; /* Port number */
+	char* database;      /* Database, case sensitive */
+};
+
+
+/*
+ * Create a new connection identifier
+ */
+struct db_id* new_db_id(const char* url);
+
+
+/*
+ * Compare two connection identifiers
+ */
+unsigned char cmp_db_id(struct db_id* id1, struct db_id* id2);
+
+
+/*
+ * Free a connection identifier
+ */
+void free_db_id(struct db_id* id);
+
+
+#endif /* _DB_ID_H */

+ 120 - 0
db/db_pool.c

@@ -0,0 +1,120 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2001-2005 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 "../dprint.h"
+#include "db_pool.h"
+
+
+/* The head of the pool */
+static struct pool_con* db_pool = 0;
+
+
+/*
+ * Search the pool for a connection with
+ * the identifier equal to id, NULL is returned
+ * when no connection is found
+ */
+struct pool_con* pool_get(struct db_id* id)
+{
+	struct pool_con* ptr;
+
+	if (!id) {
+		LOG(L_ERR, "pool_get: Invalid parameter value\n");
+		return 0;
+	}
+
+	ptr = db_pool;
+	while (ptr) {
+		if (cmp_db_id(id, ptr->id)) {
+			ptr->ref++;
+			return ptr;
+		}
+		ptr = ptr->next;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Insert a new connection into the pool
+ */
+void pool_insert(struct pool_con* con)
+{
+	if (!con) return;
+
+	con->next = db_pool;
+	db_pool = con;
+}
+
+
+/*
+ * Release connection from the pool, the function
+ * would return 1 when if the connection is not
+ * referenced anymore and thus can be closed and
+ * deleted by the backend. The function returns
+ * 0 if the connection should still be kept open
+ * because some other module is still using it.
+ * The function returns -1 if the connection is
+ * not in the pool.
+ */
+int pool_remove(struct pool_con* con)
+{
+	struct pool_con* ptr;
+
+	if (!con) return -2;
+
+	if (con->ref > 1) {
+		     /* There are still other users, just
+		      * decrease the reference count and return
+		      */
+		DBG("pool_remove: Connection still kept in the pool\n");
+		con->ref--;
+		return 0;
+	}
+
+	DBG("pool_remove: Removing connection from the pool\n");
+
+	if (db_pool == con) {
+		db_pool = db_pool->next;
+	} else {
+		ptr = db_pool;
+		while(ptr) {
+			if (ptr->next == con) break;
+			ptr = ptr->next;
+		}
+		if (!ptr) {
+			LOG(L_ERR, "pool_remove: Weird, connection not found in the pool\n");
+			return -1;
+		} else {
+			     /* Remove the connection from the pool */
+			ptr->next = con->next;
+		}
+	}
+
+	return 1;
+}

+ 77 - 0
db/db_pool.h

@@ -0,0 +1,77 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2001-2005 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 _DB_POOL_H
+#define _DB_POOL_H
+
+#include "db_id.h"
+#include "db_con.h"
+
+
+/*
+ * This is a stub that contains all attributes
+ * that pool members must have, it is not really
+ * used, real connection structures are created
+ * by database backends. All such structures (
+ * created by the backends) must have these
+ * attributes.
+ */
+struct pool_con {
+	struct db_id* id;        /* Connection identifier */
+	unsigned int ref;        /* Reference count */
+	struct pool_con* next;   /* Next element in the pool */
+};
+
+
+/*
+ * Search the pool for a connection with
+ * the identifier equal to id, NULL is returned
+ * when no connection is found
+ */
+struct pool_con* pool_get(struct db_id* id);
+
+
+/*
+ * Insert a new connection into the pool
+ */
+void pool_insert(struct pool_con* con);
+
+
+/*
+ * Release connection from the pool, the function
+ * would return 1 when if the connection is not
+ * referenced anymore and thus can be closed and
+ * deleted by the backend. The function returns
+ * 0 if the connection should still be kept open
+ * because some other module is still using it.
+ * The function returns -1 if the connection is
+ * not in the pool.
+ */
+int pool_remove(struct pool_con* con);
+
+
+#endif /* _POOL_H */