pg_con.c

Go to the documentation of this file.
00001 /* 
00002  * $Id$ 
00003  *
00004  * PostgreSQL Database Driver for SER
00005  *
00006  * Portions Copyright (C) 2001-2003 FhG FOKUS
00007  * Copyright (C) 2003 August.Net Services, LLC
00008  * Portions Copyright (C) 2005-2008 iptelorg GmbH
00009  *
00010  * This file is part of SER, a free SIP server.
00011  *
00012  * SER is free software; you can redistribute it and/or modify it under the
00013  * terms of the GNU General Public License as published by the Free Software
00014  * Foundation; either version 2 of the License, or (at your option) any later
00015  * version
00016  *
00017  * For a license to use the ser software under conditions other than those
00018  * described here, or to purchase support for this software, please contact
00019  * iptel.org by e-mail at the following addresses: info@iptel.org
00020  *
00021  * SER is distributed in the hope that it will be useful, but WITHOUT ANY
00022  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00023  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
00024  * details.
00025  *
00026  * You should have received a copy of the GNU General Public License along
00027  * with this program; if not, write to the Free Software Foundation, Inc., 59
00028  * Temple Place, Suite 330, Boston, MA 02111-1307 USA
00029  */
00030 
00039 #include "pg_con.h"
00040 #include "pg_uri.h"
00041 #include "pg_sql.h"
00042 
00043 #include "../../mem/mem.h"
00044 #include "../../dprint.h"
00045 #include "../../ut.h"
00046 
00047 #include <stdlib.h>
00048 #include <string.h>
00049 #include <netinet/in.h>
00050 #include <time.h>
00051 
00052 
00053 /* Override the default notice processor to output the messages 
00054  * using SER's output subsystem.
00055  */
00056 static void notice_processor(void* arg, const char* message)
00057 {
00058         LOG(L_NOTICE, "postgres: %s\n", message);
00059 }
00060 
00061 
00074 static int timestamp_format(PGconn* con)
00075 {
00076         unsigned long long offset;
00077         PGresult* res = 0;
00078         char* val;
00079         str sql;
00080 
00081         if (build_timestamp_format_sql(&sql) != 0) {
00082                 ERR("postgres: Error while building SQL query to obtain timestamp format\n");
00083                 return -1;
00084         }
00085         res = PQexecParams(con, sql.s, 0, 0, 0, 0, 0, 1);
00086         pkg_free(sql.s);
00087 
00088         if (PQfformat(res, 0) != 1) {
00089                 ERR("postgres: Binary format expected but server sent text\n");
00090                 goto error;
00091         }
00092 
00093         if (PQntuples(res) != 1) {
00094                 ERR("postgres: Only one column expected, %d received\n", PQntuples(res));
00095                 goto error;
00096         }
00097 
00098         if (PQnfields(res) != 1) {
00099                 ERR("postgres: Only one row expected, %d received\n", PQnfields(res));
00100                 goto error;
00101         }
00102 
00103         val = PQgetvalue(res, 0, 0);
00104         offset = ((unsigned long long)ntohl(((unsigned int*)val)[0]) << 32) 
00105                 + ntohl(((unsigned int*)val)[1]);
00106         
00107         PQclear(res);
00108 
00109         /* Server using int8 timestamps would return 1000000, because it stores
00110          * timestamps in microsecond resolution across the whole range. Server
00111          * using double timestamps would return 1 (encoded as double) here because
00112          * subsection fraction is stored as fractional part in the IEEE
00113          * representation.  1 stored as double would result in 4607182418800017408
00114          * when the memory location occupied by the variable is read as unsigned
00115          * long long.
00116          */
00117         if (offset == 1000000) {
00118                 DBG("postgres: Server uses int8 format for timestamps.\n");
00119                 return 1;
00120         } else {
00121                 DBG("postgres: Server uses double format for timestamps.\n");
00122                 return 0;
00123         }
00124         
00125  error:
00126         PQclear(res);
00127         return -1;
00128 }
00129 
00130 
00142 static int get_oids(db_con_t* con)
00143 {
00144         struct pg_con* pcon;
00145         PGresult* res = NULL;
00146         str sql;
00147 
00148         pcon = DB_GET_PAYLOAD(con);
00149         if (build_select_oid_sql(&sql) < 0) goto error;
00150         res = PQexec(pcon->con, sql.s);
00151         pkg_free(sql.s);
00152         if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) goto error;
00153         pcon->oid = pg_new_oid_table(res);
00154         PQclear(res);
00155         if (pcon->oid == NULL) goto error;
00156         return 0;
00157 
00158  error:
00159         if (res) PQclear(res);
00160         return -1;
00161 }
00162 
00163 
00170 static void pg_con_free(db_con_t* con, struct pg_con* payload)
00171 {
00172         if (!payload) return;
00173         
00174         /* Delete the structure only if there are no more references
00175          * to it in the connection pool
00176          */
00177         if (db_pool_remove((db_pool_entry_t*)payload) == 0) return;
00178         
00179         db_pool_entry_free(&payload->gen);
00180         pg_destroy_oid_table(payload->oid);
00181         if (payload->con) PQfinish(payload->con);
00182         pkg_free(payload);
00183 }
00184 
00185 
00186 int pg_con(db_con_t* con)
00187 {
00188         struct pg_con* pcon;
00189 
00190         /* First try to lookup the connection in the connection pool and
00191          * re-use it if a match is found
00192          */
00193         pcon = (struct pg_con*)db_pool_get(con->uri);
00194         if (pcon) {
00195                 DBG("postgres: Connection to %.*s:%.*s found in connection pool\n",
00196                         con->uri->scheme.len, ZSW(con->uri->scheme.s),
00197                         con->uri->body.len, ZSW(con->uri->body.s));
00198                 goto found;
00199         }
00200 
00201         pcon = (struct pg_con*)pkg_malloc(sizeof(struct pg_con));
00202         if (!pcon) {
00203                 LOG(L_ERR, "postgres: No memory left\n");
00204                 goto error;
00205         }
00206         memset(pcon, '\0', sizeof(struct pg_con));
00207         if (db_pool_entry_init(&pcon->gen, pg_con_free, con->uri) < 0) goto error;
00208 
00209         DBG("postgres: Preparing new connection to: %.*s:%.*s\n",
00210                 con->uri->scheme.len, ZSW(con->uri->scheme.s),
00211                 con->uri->body.len, ZSW(con->uri->body.s));
00212 
00213         /* Put the newly created postgres connection into the pool */
00214         db_pool_put((struct db_pool_entry*)pcon);
00215         DBG("postgres: Connection stored in connection pool\n");
00216 
00217  found:
00218         /* Attach driver payload to the db_con structure and set connect and
00219          * disconnect functions
00220          */
00221         DB_SET_PAYLOAD(con, pcon);
00222         con->connect = pg_con_connect;
00223         con->disconnect = pg_con_disconnect;
00224         return 0;
00225 
00226  error:
00227         if (pcon) {
00228                 db_pool_entry_free(&pcon->gen);
00229                 pkg_free(pcon);
00230         }
00231         return -1;
00232 }
00233 
00234 
00235 int pg_con_connect(db_con_t* con)
00236 {
00237         struct pg_con* pcon;
00238         struct pg_uri* puri;
00239         char* port_str;
00240         int ret;
00241         
00242         pcon = DB_GET_PAYLOAD(con);
00243         puri = DB_GET_PAYLOAD(con->uri);
00244         
00245         /* Do not reconnect already connected connections */
00246         if (pcon->flags & PG_CONNECTED) return 0;
00247 
00248         DBG("postgres: Connecting to %.*s:%.*s\n",
00249                 con->uri->scheme.len, ZSW(con->uri->scheme.s),
00250                 con->uri->body.len, ZSW(con->uri->body.s));
00251 
00252         if (puri->port > 0) {
00253                 port_str = int2str(puri->port, 0);
00254         } else {
00255                 port_str = NULL;
00256         }
00257 
00258         if (pcon->con) {
00259                 PQfinish(pcon->con);
00260                 pcon->con = NULL;
00261         }
00262 
00263         pcon->con = PQsetdbLogin(puri->host, port_str,
00264                                                          NULL, NULL, puri->database,
00265                                                          puri->username, puri->password);
00266         
00267         if (pcon->con == NULL) {
00268                 ERR("postgres: PQsetdbLogin ran out of memory\n");
00269                 goto error;
00270         }
00271         
00272         if (PQstatus(pcon->con) != CONNECTION_OK) {
00273                 ERR("postgres: %s\n", PQerrorMessage(pcon->con));
00274                 goto error;
00275         }
00276         
00277         /* Override default notice processor */
00278         PQsetNoticeProcessor(pcon->con, notice_processor, 0);
00279         
00280 #ifdef HAVE_PGSERVERVERSION
00281         DBG("postgres: Connected. Protocol version=%d, Server version=%d\n", 
00282             PQprotocolVersion(pcon->con), PQserverVersion(pcon->con));
00283 #else
00284         DBG("postgres: Connected. Protocol version=%d, Server version=%d\n", 
00285             PQprotocolVersion(pcon->con), 0 );
00286 #endif
00287 
00288         ret = timestamp_format(pcon->con);
00289         if (ret == 1 || ret == -1) {
00290                 /* Assume INT8 representation if detection fails */
00291                 pcon->flags |= PG_INT8_TIMESTAMP;
00292         } else {
00293                 pcon->flags &= ~PG_INT8_TIMESTAMP;
00294         }
00295 
00296         if (get_oids(con) < 0) goto error;
00297 
00298         pcon->flags |= PG_CONNECTED;
00299         return 0;
00300 
00301  error:
00302         if (pcon->con) PQfinish(pcon->con);
00303         pcon->con = NULL;
00304         return -1;
00305 }
00306 
00307 
00308 void pg_con_disconnect(db_con_t* con)
00309 {
00310         struct pg_con* pcon;
00311 
00312         pcon = DB_GET_PAYLOAD(con);
00313         if ((pcon->flags & PG_CONNECTED) == 0) return;
00314 
00315         DBG("postgres: Disconnecting from %.*s:%.*s\n",
00316                 con->uri->scheme.len, ZSW(con->uri->scheme.s),
00317                 con->uri->body.len, ZSW(con->uri->body.s));
00318 
00319         PQfinish(pcon->con);
00320         pcon->con = NULL;
00321         pcon->flags &= ~PG_CONNECTED;
00322         pcon->flags &= ~PG_INT8_TIMESTAMP;
00323 }
00324