851685d3532eb480d6706025df306c43a05caa7f
[sip-router] / src / modules / db_postgres / pg_con.c
1 /* 
2  * PostgreSQL Database Driver for Kamailio
3  *
4  * Portions Copyright (C) 2001-2003 FhG FOKUS
5  * Copyright (C) 2003 August.Net Services, LLC
6  * Portions Copyright (C) 2005-2008 iptelorg GmbH
7  *
8  * This file is part of Kamailio, a free SIP server.
9  *
10  * Kamailio is free software; you can redistribute it and/or modify it under the
11  * terms of the GNU General Public License as published by the Free Software
12  * Foundation; either version 2 of the License, or (at your option) any later
13  * version
14  *
15  * Kamailio is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc., 59
22  * Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  */
24
25 /** \addtogroup postgres
26  * @{ 
27  */
28
29 /** \file 
30  * Functions related to connections to PostgreSQL servers.
31  */
32
33 #include "pg_con.h"
34 #include "pg_uri.h"
35 #include "pg_sql.h"
36 #include "pg_mod.h"
37
38 #include "../../core/mem/mem.h"
39 #include "../../core/dprint.h"
40 #include "../../core/ut.h"
41
42 #include <stdlib.h>
43 #include <string.h>
44 #include <netinet/in.h>
45 #include <netinet/tcp.h>
46 #include <time.h>
47
48
49 /* Override the default notice processor to output the messages 
50  * using SER's output subsystem.
51  */
52 static void notice_processor(void* arg, const char* message)
53 {
54         LOG(L_NOTICE, "postgres: %s\n", message);
55 }
56
57
58 /** Determine the format of timestamps used by the server.  
59  * A PostgresSQL server can be configured to store timestamps either as 8-byte
60  * integers or floating point numbers with double precision. This functions
61  * sends a simple SQL query to the server and tries to determine the format of
62  * timestamps from the reply. This function is executed once after connecting
63  * to a PostgreSQL server and the result of the detection is then stored in
64  * form of a flag in pg_con connection structure.
65  * @param con A PostgreSQL connection handle
66  * @retval 0 If the server stores timestamps as floating point numbers.
67  * @retval 1 If the server stores timestamps as 8-byte integers.
68  * @retval A negative number on error.
69  */
70 static int timestamp_format(PGconn* con)
71 {
72         unsigned long long offset;
73         PGresult* res = 0;
74         char* val;
75         str sql;
76
77         if (build_timestamp_format_sql(&sql) != 0) {
78                 ERR("postgres: Error while building SQL query to obtain timestamp format\n");
79                 return -1;
80         }
81         res = PQexecParams(con, sql.s, 0, 0, 0, 0, 0, 1);
82         pkg_free(sql.s);
83
84         if (PQfformat(res, 0) != 1) {
85                 ERR("postgres: Binary format expected but server sent text\n");
86                 goto error;
87         }
88
89         if (PQntuples(res) != 1) {
90                 ERR("postgres: Only one column expected, %d received\n", PQntuples(res));
91                 goto error;
92         }
93
94         if (PQnfields(res) != 1) {
95                 ERR("postgres: Only one row expected, %d received\n", PQnfields(res));
96                 goto error;
97         }
98
99         val = PQgetvalue(res, 0, 0);
100         offset = ((unsigned long long)ntohl(((unsigned int*)val)[0]) << 32) 
101                 + ntohl(((unsigned int*)val)[1]);
102         
103         PQclear(res);
104
105         /* Server using int8 timestamps would return 1000000, because it stores
106          * timestamps in microsecond resolution across the whole range. Server
107          * using double timestamps would return 1 (encoded as double) here because
108          * subsection fraction is stored as fractional part in the IEEE
109          * representation.  1 stored as double would result in 4607182418800017408
110          * when the memory location occupied by the variable is read as unsigned
111          * long long.
112          */
113         if (offset == 1000000) {
114                 DBG("postgres: Server uses int8 format for timestamps.\n");
115                 return 1;
116         } else {
117                 DBG("postgres: Server uses double format for timestamps.\n");
118                 return 0;
119         }
120         
121  error:
122         PQclear(res);
123         return -1;
124 }
125
126
127 /** Retrieves a list of all supported field types from the server.
128  * This function retrieves a list of all supported field types and their Oids
129  * from system catalogs of the server. The list is then stored in pg_con
130  * connection structure and it is used to map field type names, such as int2,
131  * int4, float4, etc. to Oids. Every PostgreSQL server can map field types to
132  * different Oids so we need to store the mapping array in the connection
133  * structure.
134  * @param con A structure representing connection to PostgreSQL server.
135  * @retval 0 If executed successfully.
136  * @retval A negative number on error.
137  */
138 static int get_oids(db_con_t* con)
139 {
140         struct pg_con* pcon;
141         PGresult* res = NULL;
142         str sql;
143
144         pcon = DB_GET_PAYLOAD(con);
145         if (build_select_oid_sql(&sql) < 0) goto error;
146         res = PQexec(pcon->con, sql.s);
147         pkg_free(sql.s);
148         if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) goto error;
149         pcon->oid = pg_new_oid_table(res);
150         PQclear(res);
151         if (pcon->oid == NULL) goto error;
152         return 0;
153
154  error:
155         if (res) PQclear(res);
156         return -1;
157 }
158
159
160 /** Free all memory allocated for a pg_con structure.
161  * This function function frees all memory that is in use by
162  * a pg_con structure.
163  * @param con A generic db_con connection structure.
164  * @param payload PostgreSQL specific payload to be freed.
165  */
166 static void pg_con_free(db_con_t* con, struct pg_con* payload)
167 {
168         if (!payload) return;
169         
170         /* Delete the structure only if there are no more references
171          * to it in the connection pool
172          */
173         if (db_pool_remove((db_pool_entry_t*)payload) == 0) return;
174         
175         db_pool_entry_free(&payload->gen);
176         pg_destroy_oid_table(payload->oid);
177         if (payload->con) PQfinish(payload->con);
178         pkg_free(payload);
179 }
180
181
182 int pg_con(db_con_t* con)
183 {
184         struct pg_con* pcon;
185
186         /* First try to lookup the connection in the connection pool and
187          * re-use it if a match is found
188          */
189         pcon = (struct pg_con*)db_pool_get(con->uri);
190         if (pcon) {
191                 DBG("postgres: Connection to %.*s:%.*s found in connection pool\n",
192                         con->uri->scheme.len, ZSW(con->uri->scheme.s),
193                         con->uri->body.len, ZSW(con->uri->body.s));
194                 goto found;
195         }
196
197         pcon = (struct pg_con*)pkg_malloc(sizeof(struct pg_con));
198         if (!pcon) {
199                 LOG(L_ERR, "postgres: No memory left\n");
200                 goto error;
201         }
202         memset(pcon, '\0', sizeof(struct pg_con));
203         if (db_pool_entry_init(&pcon->gen, pg_con_free, con->uri) < 0) goto error;
204
205         DBG("postgres: Preparing new connection to: %.*s:%.*s\n",
206                 con->uri->scheme.len, ZSW(con->uri->scheme.s),
207                 con->uri->body.len, ZSW(con->uri->body.s));
208
209         /* Put the newly created postgres connection into the pool */
210         db_pool_put((struct db_pool_entry*)pcon);
211         DBG("postgres: Connection stored in connection pool\n");
212
213  found:
214         /* Attach driver payload to the db_con structure and set connect and
215          * disconnect functions
216          */
217         DB_SET_PAYLOAD(con, pcon);
218         con->connect = pg_con_connect;
219         con->disconnect = pg_con_disconnect;
220         return 0;
221
222  error:
223         if (pcon) {
224                 db_pool_entry_free(&pcon->gen);
225                 pkg_free(pcon);
226         }
227         return -1;
228 }
229
230
231 int pg_con_connect(db_con_t* con)
232 {
233         struct pg_con* pcon;
234         struct pg_uri* puri;
235         char* port_str;
236         int ret, i = 0;
237         const char *keywords[10], *values[10];
238         char to[16];
239         
240         pcon = DB_GET_PAYLOAD(con);
241         puri = DB_GET_PAYLOAD(con->uri);
242         
243         /* Do not reconnect already connected connections */
244         if (pcon->flags & PG_CONNECTED) return 0;
245
246         DBG("postgres: Connecting to %.*s:%.*s\n",
247                 con->uri->scheme.len, ZSW(con->uri->scheme.s),
248                 con->uri->body.len, ZSW(con->uri->body.s));
249
250         if (puri->port > 0) {
251                 port_str = int2str(puri->port, 0);
252                 keywords[i] = "port";
253                 values[i++] = port_str;
254         } else {
255                 port_str = NULL;
256         }
257
258         if (pcon->con) {
259                 PQfinish(pcon->con);
260                 pcon->con = NULL;
261         }
262
263         keywords[i] = "host";
264         values[i++] = puri->host;
265         keywords[i] = "dbname";
266         values[i++] = puri->database;
267         keywords[i] = "user";
268         values[i++] = puri->username;
269         keywords[i] = "password";
270         values[i++] = puri->password;
271         if (pg_timeout > 0) {
272                 snprintf(to, sizeof(to)-1, "%d", pg_timeout + 3);
273                 keywords[i] = "connect_timeout";
274                 values[i++] = to;
275         }
276
277         keywords[i] = values[i] = NULL;
278
279         pcon->con = PQconnectdbParams(keywords, values, 1);
280         
281         if (pcon->con == NULL) {
282                 ERR("postgres: PQconnectdbParams ran out of memory\n");
283                 goto error;
284         }
285         
286         if (PQstatus(pcon->con) != CONNECTION_OK) {
287                 ERR("postgres: %s\n", PQerrorMessage(pcon->con));
288                 goto error;
289         }
290         
291         /* Override default notice processor */
292         PQsetNoticeProcessor(pcon->con, notice_processor, 0);
293         
294 #ifdef HAVE_PGSERVERVERSION
295         DBG("postgres: Connected. Protocol version=%d, Server version=%d\n", 
296             PQprotocolVersion(pcon->con), PQserverVersion(pcon->con));
297 #else
298         DBG("postgres: Connected. Protocol version=%d, Server version=%d\n", 
299             PQprotocolVersion(pcon->con), 0 );
300 #endif
301
302 #if defined(SO_KEEPALIVE) && defined(TCP_KEEPIDLE)
303         if (pg_keepalive) {
304                 i = 1;
305                 if(setsockopt(PQsocket(pcon->con), SOL_SOCKET, SO_KEEPALIVE, &i,
306                                 sizeof(i))<0) {
307                         LM_WARN("failed to set socket option keepalive\n");
308                 }
309                 if(setsockopt(PQsocket(pcon->con), IPPROTO_TCP, TCP_KEEPIDLE,
310                                 &pg_keepalive, sizeof(pg_keepalive))<0) {
311                         M_WARN("failed to set socket option keepidle\n");
312                 }
313         }
314 #endif
315
316         ret = timestamp_format(pcon->con);
317         if (ret == 1 || ret == -1) {
318                 /* Assume INT8 representation if detection fails */
319                 pcon->flags |= PG_INT8_TIMESTAMP;
320         } else {
321                 pcon->flags &= ~PG_INT8_TIMESTAMP;
322         }
323
324         if (get_oids(con) < 0) goto error;
325
326         pcon->flags |= PG_CONNECTED;
327         return 0;
328
329  error:
330         if (pcon->con) PQfinish(pcon->con);
331         pcon->con = NULL;
332         return -1;
333 }
334
335
336 void pg_con_disconnect(db_con_t* con)
337 {
338         struct pg_con* pcon;
339
340         pcon = DB_GET_PAYLOAD(con);
341         if ((pcon->flags & PG_CONNECTED) == 0) return;
342
343         DBG("postgres: Disconnecting from %.*s:%.*s\n",
344                 con->uri->scheme.len, ZSW(con->uri->scheme.s),
345                 con->uri->body.len, ZSW(con->uri->body.s));
346
347         PQfinish(pcon->con);
348         pcon->con = NULL;
349         pcon->flags &= ~PG_CONNECTED;
350         pcon->flags &= ~PG_INT8_TIMESTAMP;
351 }
352
353 /** @} */