Browse code

- improved database connection pool

Jan Janak authored on 02/02/2005 19:08:43
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,274 @@
1
+/* 
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2001-2005 iptel.org
5
+ *
6
+ * This file is part of ser, a free SIP server.
7
+ *
8
+ * ser is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * For a license to use the ser software under conditions
14
+ * other than those described here, or to purchase support for this
15
+ * software, please contact iptel.org by e-mail at the following addresses:
16
+ *    info@iptel.org
17
+ *
18
+ * ser is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License 
24
+ * along with this program; if not, write to the Free Software 
25
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
+ */
27
+
28
+#include "db_id.h"
29
+#include "../dprint.h"
30
+#include "../mem/mem.h"
31
+#include "../ut.h"
32
+#include <stdlib.h>
33
+#include <string.h>
34
+
35
+
36
+/*
37
+ * Duplicate a string
38
+ */
39
+static int dupl_string(char** dst, const char* begin, const char* end)
40
+{
41
+	if (*dst) pkg_free(*dst);
42
+
43
+	*dst = pkg_malloc(end - begin + 1);
44
+	if ((*dst) == NULL) {
45
+		return -1;
46
+	}
47
+
48
+	memcpy(*dst, begin, end - begin);
49
+	(*dst)[end - begin] = '\0';
50
+	return 0;
51
+}
52
+
53
+
54
+/*
55
+ * Parse a database URL of form 
56
+ * scheme://[username[:password]@]hostname[:port]/database
57
+ *
58
+ * Returns 0 if parsing was successful and -1 otherwise
59
+ */
60
+static int parse_db_url(struct db_id* id, const char* url)
61
+{
62
+#define SHORTEST_DB_URL "s://a/b"
63
+#define SHORTEST_DB_URL_LEN (sizeof(SHORTEST_DB_URL) - 1)
64
+
65
+	enum state {
66
+		ST_SCHEME,     /* Scheme part */
67
+		ST_SLASH1,     /* First slash */
68
+		ST_SLASH2,     /* Second slash */
69
+		ST_USER_HOST,  /* Username or hostname */
70
+		ST_PASS_PORT,  /* Password or port part */
71
+		ST_HOST,       /* Hostname part */
72
+		ST_PORT,       /* Port part */
73
+		ST_DB          /* Database part */
74
+	};
75
+
76
+	enum state st;
77
+	int len, i;
78
+	const char* begin;
79
+	char* prev_token;
80
+
81
+	prev_token = 0;
82
+
83
+	if (!id || !url) {
84
+		goto err;
85
+	}
86
+	
87
+	len = strlen(url);
88
+	if (len < SHORTEST_DB_URL_LEN) {
89
+		goto err;
90
+	}
91
+	
92
+	     /* Initialize all attributes to 0 */
93
+	memset(id, 0, sizeof(struct db_id));
94
+	st = ST_SCHEME;
95
+	begin = url;
96
+
97
+	for(i = 0; i < len; i++) {
98
+		switch(st) {
99
+		case ST_SCHEME:
100
+			switch(url[i]) {
101
+			case ':':
102
+				st = ST_SLASH1;
103
+				if (dupl_string(&id->scheme, begin, url + i) < 0) goto err;
104
+				break;
105
+			}
106
+			break;
107
+
108
+		case ST_SLASH1:
109
+			switch(url[i]) {
110
+			case '/':
111
+				st = ST_SLASH2;
112
+				break;
113
+
114
+			default:
115
+				goto err;
116
+			}
117
+			break;
118
+
119
+		case ST_SLASH2:
120
+			switch(url[i]) {
121
+			case '/':
122
+				st = ST_USER_HOST;
123
+				begin = url + i + 1;
124
+				break;
125
+				
126
+			default:
127
+				goto err;
128
+			}
129
+			break;
130
+
131
+		case ST_USER_HOST:
132
+			switch(url[i]) {
133
+			case '@':
134
+				st = ST_HOST;
135
+				if (dupl_string(&id->username, begin, url + i) < 0) goto err;
136
+				begin = url + i + 1;
137
+				break;
138
+
139
+			case ':':
140
+				st = ST_PASS_PORT;
141
+				if (dupl_string(&prev_token, begin, url + i) < 0) goto err;
142
+				begin = url + i + 1;
143
+				break;
144
+
145
+			case '/':
146
+				if (dupl_string(&id->host, begin, url + i) < 0) goto err;
147
+				if (dupl_string(&id->database, url + i + 1, url + len) < 0) goto err;
148
+				return 0;
149
+			}
150
+			break;
151
+
152
+		case ST_PASS_PORT:
153
+			switch(url[i]) {
154
+			case '@':
155
+				st = ST_HOST;
156
+				id->username = prev_token;
157
+				if (dupl_string(&id->password, begin, url + i) < 0) goto err;
158
+				begin = url + i + 1;
159
+				break;
160
+
161
+			case '/':
162
+				id->host = prev_token;
163
+				id->port = str2s(begin, url + i - begin, 0);
164
+				if (dupl_string(&id->database, url + i + 1, url + len) < 0) goto err;
165
+				return 0;
166
+			}
167
+			break;
168
+
169
+		case ST_HOST:
170
+			switch(url[i]) {
171
+			case ':':
172
+				st = ST_PORT;
173
+				if (dupl_string(&id->host, begin, url + i) < 0) goto err;
174
+				begin = url + i + 1;
175
+				break;
176
+
177
+			case '/':
178
+				if (dupl_string(&id->host, begin, url + i) < 0) goto err;
179
+				if (dupl_string(&id->database, url + i + 1, url + len) < 0) goto err;
180
+				return 0;
181
+			}
182
+			break;
183
+
184
+		case ST_PORT:
185
+			switch(url[i]) {
186
+			case '/':
187
+				id->port = str2s(begin, url + i - begin, 0);
188
+				if (dupl_string(&id->database, url + i + 1, url + len) < 0) goto err;
189
+				return 0;
190
+			}
191
+			break;
192
+			
193
+		case ST_DB:
194
+			break;
195
+		}
196
+	}
197
+
198
+	if (st != ST_DB) goto err;
199
+	return 0;
200
+
201
+ err:
202
+	if (id->scheme) pkg_free(id->scheme);
203
+	if (id->username) pkg_free(id->username);
204
+	if (id->password) pkg_free(id->password);
205
+	if (id->host) pkg_free(id->host);
206
+	if (id->database) pkg_free(id->database);
207
+	if (prev_token) pkg_free(prev_token);
208
+	return -1;
209
+}
210
+
211
+
212
+/*
213
+ * Create a new connection identifier
214
+ */
215
+struct db_id* new_db_id(const char* url)
216
+{
217
+	struct db_id* ptr;
218
+
219
+	if (!url) {
220
+		LOG(L_ERR, "new_db_id: Invalid parameter\n");
221
+		return 0;
222
+	}
223
+
224
+	ptr = (struct db_id*)pkg_malloc(sizeof(struct db_id));
225
+	if (!ptr) {
226
+		LOG(L_ERR, "new_db_id: No memory left\n");
227
+		goto err;
228
+	}
229
+	memset(ptr, 0, sizeof(struct db_id));
230
+
231
+	if (parse_db_url(ptr, url) < 0) {
232
+		LOG(L_ERR, "new_db_id: Error while parsing database URL: %s\n", url);
233
+		goto err;
234
+	}
235
+
236
+	return ptr;
237
+
238
+ err:
239
+	if (ptr) pkg_free(ptr);
240
+	return 0;
241
+}
242
+
243
+
244
+/*
245
+ * Compare two connection identifiers
246
+ */
247
+unsigned char cmp_db_id(struct db_id* id1, struct db_id* id2)
248
+{
249
+	if (!id1 || !id2) return 0;
250
+	if (id1->port != id2->port) return 0;
251
+
252
+	if (strcmp(id1->scheme, id2->scheme)) return 0;
253
+	if (strcmp(id1->username, id2->username)) return 0;
254
+	if (strcmp(id1->password, id2->password)) return 0;
255
+	if (strcasecmp(id1->host, id2->host)) return 0;
256
+	if (strcmp(id1->database, id2->database)) return 0;
257
+	return 1;
258
+}
259
+
260
+
261
+/*
262
+ * Free a connection identifier
263
+ */
264
+void free_db_id(struct db_id* id)
265
+{
266
+	if (!id) return;
267
+
268
+	if (id->scheme) pkg_free(id->scheme);
269
+	if (id->username) pkg_free(id->username);
270
+	if (id->password) pkg_free(id->password);
271
+	if (id->host) pkg_free(id->host);
272
+	if (id->database) pkg_free(id->database);
273
+	pkg_free(id);
274
+}
0 275
new file mode 100644
... ...
@@ -0,0 +1,62 @@
1
+/* 
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2001-2005 iptel.org
5
+ *
6
+ * This file is part of ser, a free SIP server.
7
+ *
8
+ * ser is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * For a license to use the ser software under conditions
14
+ * other than those described here, or to purchase support for this
15
+ * software, please contact iptel.org by e-mail at the following addresses:
16
+ *    info@iptel.org
17
+ *
18
+ * ser is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License 
24
+ * along with this program; if not, write to the Free Software 
25
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
+ */
27
+
28
+#ifndef _DB_ID_H
29
+#define _DB_ID_H
30
+
31
+#include "../str.h"
32
+
33
+
34
+struct db_id {
35
+	char* scheme;        /* URL scheme */
36
+	char* username;      /* Username, case sensitive */
37
+	char* password;      /* Password, case sensitive */
38
+	char* host;          /* Host or IP, case insensitive */
39
+	unsigned short port; /* Port number */
40
+	char* database;      /* Database, case sensitive */
41
+};
42
+
43
+
44
+/*
45
+ * Create a new connection identifier
46
+ */
47
+struct db_id* new_db_id(const char* url);
48
+
49
+
50
+/*
51
+ * Compare two connection identifiers
52
+ */
53
+unsigned char cmp_db_id(struct db_id* id1, struct db_id* id2);
54
+
55
+
56
+/*
57
+ * Free a connection identifier
58
+ */
59
+void free_db_id(struct db_id* id);
60
+
61
+
62
+#endif /* _DB_ID_H */
0 63
new file mode 100644
... ...
@@ -0,0 +1,120 @@
1
+/* 
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2001-2005 iptel.org
5
+ *
6
+ * This file is part of ser, a free SIP server.
7
+ *
8
+ * ser is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * For a license to use the ser software under conditions
14
+ * other than those described here, or to purchase support for this
15
+ * software, please contact iptel.org by e-mail at the following addresses:
16
+ *    info@iptel.org
17
+ *
18
+ * ser is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License 
24
+ * along with this program; if not, write to the Free Software 
25
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
+ */
27
+
28
+#include "../dprint.h"
29
+#include "db_pool.h"
30
+
31
+
32
+/* The head of the pool */
33
+static struct pool_con* db_pool = 0;
34
+
35
+
36
+/*
37
+ * Search the pool for a connection with
38
+ * the identifier equal to id, NULL is returned
39
+ * when no connection is found
40
+ */
41
+struct pool_con* pool_get(struct db_id* id)
42
+{
43
+	struct pool_con* ptr;
44
+
45
+	if (!id) {
46
+		LOG(L_ERR, "pool_get: Invalid parameter value\n");
47
+		return 0;
48
+	}
49
+
50
+	ptr = db_pool;
51
+	while (ptr) {
52
+		if (cmp_db_id(id, ptr->id)) {
53
+			ptr->ref++;
54
+			return ptr;
55
+		}
56
+		ptr = ptr->next;
57
+	}
58
+
59
+	return 0;
60
+}
61
+
62
+
63
+/*
64
+ * Insert a new connection into the pool
65
+ */
66
+void pool_insert(struct pool_con* con)
67
+{
68
+	if (!con) return;
69
+
70
+	con->next = db_pool;
71
+	db_pool = con;
72
+}
73
+
74
+
75
+/*
76
+ * Release connection from the pool, the function
77
+ * would return 1 when if the connection is not
78
+ * referenced anymore and thus can be closed and
79
+ * deleted by the backend. The function returns
80
+ * 0 if the connection should still be kept open
81
+ * because some other module is still using it.
82
+ * The function returns -1 if the connection is
83
+ * not in the pool.
84
+ */
85
+int pool_remove(struct pool_con* con)
86
+{
87
+	struct pool_con* ptr;
88
+
89
+	if (!con) return -2;
90
+
91
+	if (con->ref > 1) {
92
+		     /* There are still other users, just
93
+		      * decrease the reference count and return
94
+		      */
95
+		DBG("pool_remove: Connection still kept in the pool\n");
96
+		con->ref--;
97
+		return 0;
98
+	}
99
+
100
+	DBG("pool_remove: Removing connection from the pool\n");
101
+
102
+	if (db_pool == con) {
103
+		db_pool = db_pool->next;
104
+	} else {
105
+		ptr = db_pool;
106
+		while(ptr) {
107
+			if (ptr->next == con) break;
108
+			ptr = ptr->next;
109
+		}
110
+		if (!ptr) {
111
+			LOG(L_ERR, "pool_remove: Weird, connection not found in the pool\n");
112
+			return -1;
113
+		} else {
114
+			     /* Remove the connection from the pool */
115
+			ptr->next = con->next;
116
+		}
117
+	}
118
+
119
+	return 1;
120
+}
0 121
new file mode 100644
... ...
@@ -0,0 +1,77 @@
1
+/* 
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2001-2005 iptel.org
5
+ *
6
+ * This file is part of ser, a free SIP server.
7
+ *
8
+ * ser is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * For a license to use the ser software under conditions
14
+ * other than those described here, or to purchase support for this
15
+ * software, please contact iptel.org by e-mail at the following addresses:
16
+ *    info@iptel.org
17
+ *
18
+ * ser is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License 
24
+ * along with this program; if not, write to the Free Software 
25
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
+ */
27
+
28
+#ifndef _DB_POOL_H
29
+#define _DB_POOL_H
30
+
31
+#include "db_id.h"
32
+#include "db_con.h"
33
+
34
+
35
+/*
36
+ * This is a stub that contains all attributes
37
+ * that pool members must have, it is not really
38
+ * used, real connection structures are created
39
+ * by database backends. All such structures (
40
+ * created by the backends) must have these
41
+ * attributes.
42
+ */
43
+struct pool_con {
44
+	struct db_id* id;        /* Connection identifier */
45
+	unsigned int ref;        /* Reference count */
46
+	struct pool_con* next;   /* Next element in the pool */
47
+};
48
+
49
+
50
+/*
51
+ * Search the pool for a connection with
52
+ * the identifier equal to id, NULL is returned
53
+ * when no connection is found
54
+ */
55
+struct pool_con* pool_get(struct db_id* id);
56
+
57
+
58
+/*
59
+ * Insert a new connection into the pool
60
+ */
61
+void pool_insert(struct pool_con* con);
62
+
63
+
64
+/*
65
+ * Release connection from the pool, the function
66
+ * would return 1 when if the connection is not
67
+ * referenced anymore and thus can be closed and
68
+ * deleted by the backend. The function returns
69
+ * 0 if the connection should still be kept open
70
+ * because some other module is still using it.
71
+ * The function returns -1 if the connection is
72
+ * not in the pool.
73
+ */
74
+int pool_remove(struct pool_con* con);
75
+
76
+
77
+#endif /* _POOL_H */