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