浏览代码

Merge db_postgres module from kamailio/trunk into db_postgres modules
in sip-router.

* kamailio/db_postgres: (58 commits)
- fix regression during value conversion when input SQL string is NULL,
- another error condition fix for a problem that gets introduced by a
- add one DBG log to each drivers error condition, that the mem is freed
- improve two errors messages, inform the user that the query is aborted
- db_postgres_convert_rows needs to free the row_buf on all error conditions
- partial revert of commit rev5359 for db_mysql module
- fix a few line breaks in errors logs
- unify common rows and row allocation functionality in the DB API core
- remove LM_ERR probe that slipped into previous commit
- change behaviour of db_str2val, this now copy strings
- add 'db_postgres' prefix to free_query function
- fix a bunch of doxygen errors (mostly in modules, some in the core)
- fix a bunch of errors in doxygen
- fix memory leak in db_postgres module related to BLOBs (also caused an
- fix postgres NULL value behaviour: as in postgres a NULL value is
- docs extension: explain fetch_result functionality better to prevent errors
- remove not reached return statement at the end of val2str functions
- make small wrapper around PQclear void, nobody checks the return state
- doxygen conversion, write new documentation, small cleanups
- initial support for BIGINT database in DB core and SQL based database
...

Jan Janak 16 年之前
父节点
当前提交
bd30d4f936

+ 36 - 0
modules/db_postgres/km_Makefile

@@ -0,0 +1,36 @@
+# $Id$
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=db_postgres.so
+
+
+# set CROSS_COMPILE to true if you want to skip
+# the autodetection
+# CROSS_COMPILE=true
+
+ifeq ($(CROSS_COMPILE),)
+PGCFG=$(shell which pg_config)
+endif
+
+ifneq ($(PGCFG),)
+
+	# use autodetection
+	DEFS += -I$(shell $(PGCFG) --includedir)
+	LIBS = -L$(shell $(PGCFG) --libdir) -lpq
+
+else
+
+	# use standard know paths
+	# libpq-fe.h locations
+	DEFS +=-I$(LOCALBASE)/include -I$(LOCALBASE)/pgsql/include \
+		 -I$(SYSBASE)/include/pgsql -I$(SYSBASE)/include/postgresql \
+		 -I$(SYSBASE)/include/postgresql/8.0
+	LIBS=-L$(LOCALBASE)/lib -L$(LOCALBASE)/pgsql/lib -L$(LOCALBASE)/lib/pgsql \
+		 -lpq
+
+endif
+
+include ../../Makefile.modules

+ 62 - 0
modules/db_postgres/km_README

@@ -0,0 +1,62 @@
+postgres Module
+
+Greg Fausak
+
+   August.net
+
+Edited by
+
+Greg Fausak
+
+   Copyright © 2003 Greg Fausak
+   Revision History
+   Revision $Revision$ $Date: 2008-08-06 12:08:33 +0200
+                              (Mi, 06 Aug 2008) $
+     __________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1.1. Overview
+        1.2. Dependencies
+
+              1.2.1. Kamailio Modules
+              1.2.2. External Libraries or Applications
+
+        1.3. Exported Parameters
+        1.4. Exported Functions
+        1.5. Installation and Running
+
+Chapter 1. Admin Guide
+
+1.1. Overview
+
+   Module description
+
+1.2. Dependencies
+
+1.2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * No dependencies on other Kamailio modules.
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed
+   before running Kamailio with this module loaded:
+     * PostgreSQL library - e.g., libpq5.
+     * PostgreSQL devel library - to compile the module (e.g.,
+       libpq-dev).
+
+1.3. Exported Parameters
+
+   NONE
+
+1.4. Exported Functions
+
+   NONE
+
+1.5. Installation and Running
+
+   Notes about installation and running.

+ 100 - 0
modules/db_postgres/km_db_postgres.c

@@ -0,0 +1,100 @@
+/*
+ * $Id$ 
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ */
+
+/*! \file
+ *  \brief DB_POSTGRES :: Core
+ *  \ingroup db_postgres
+ *  Module: \ref db_postgres
+ */
+
+/*! \defgroup db_postgres DB_POSTGRES :: the PostgreSQL driver for Kamailio
+ *  \brief The Kamailio database interface to the PostgreSQL database
+ *  - http://www.postgresql.org
+ *
+ */
+
+#include <stdio.h>
+#include "../../sr_module.h"
+#include "../../db/db_con.h"
+#include "../../db/db.h"
+#include "dbase.h"
+
+
+MODULE_VERSION
+
+int db_postgres_bind_api(db_func_t *dbb);
+
+static int mod_init(void);
+
+/*
+ * PostgreSQL database module interface
+ */
+
+static cmd_export_t cmds[]={
+	{"db_bind_api",     (cmd_function)db_postgres_bind_api,     0, 0, 0, 0},
+	{0,0,0,0,0,0}
+};
+
+
+
+struct module_exports exports = {
+	"db_postgres",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	0,        /*  module parameters */
+	0,        /* exported statistics */
+	0,        /* exported MI functions */
+	0,        /* exported pseudo-variables */
+	0,        /* extra processes */
+	mod_init, /* module initialization function */
+	0,        /* response function*/
+	0,        /* destroy function */
+	0         /* per-child init function */
+};
+
+
+static int mod_init(void)
+{
+	return 0;
+}
+
+int db_postgres_bind_api(db_func_t *dbb)
+{
+	if(dbb==NULL)
+		return -1;
+
+	memset(dbb, 0, sizeof(db_func_t));
+
+	dbb->use_table        = db_postgres_use_table;
+	dbb->init             = db_postgres_init;
+	dbb->close            = db_postgres_close;
+	dbb->query            = db_postgres_query;
+	dbb->fetch_result     = db_postgres_fetch_result;
+	dbb->raw_query        = db_postgres_raw_query;
+	dbb->free_result      = db_postgres_free_result;
+	dbb->insert           = db_postgres_insert;
+	dbb->delete           = db_postgres_delete; 
+	dbb->update           = db_postgres_update;
+
+	return 0;
+}

+ 547 - 0
modules/db_postgres/km_dbase.c

@@ -0,0 +1,547 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Copyright (C) 2006 Norman Brandinger
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * History
+ * -------
+ * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
+ * 2006-07-28 within pg_get_result(): added check to immediatly return of no 
+ *            result set was returned added check to only execute 
+ *            convert_result() if PGRES_TUPLES_OK added safety check to avoid 
+ *            double pg_free_result() (norm)
+ * 2006-08-07 Rewrote pg_get_result().
+ *            Additional debugging lines have been placed through out the code.
+ *            Added Asynchronous Command Processing (PQsendQuery/PQgetResult) 
+ *            instead of PQexec. this was done in preparation of adding FETCH 
+ *            support.  Note that PQexec returns a result pointer while 
+ *            PQsendQuery does not.  The result set pointer is obtained from 
+ *            a call (or multiple calls) to PQgetResult.
+ *            Removed transaction processing calls (BEGIN/COMMIT/ROLLBACK) as 
+ *            they added uneeded overhead.  Klaus' testing showed in excess of 
+ *            1ms gain by removing each command.  In addition, Kamailio only 
+ *            issues single queries and is not, at this time transaction aware.
+ *            The transaction processing routines have been left in place 
+ *            should this support be needed in the future.
+ *            Updated logic in pg_query / pg_raw_query to accept a (0) result 
+ *            set (_r) parameter.  In this case, control is returned
+ *            immediately after submitting the query and no call to 
+ *            pg_get_results() is performed. This is a requirement for 
+ *            FETCH support. (norm)
+ * 2006-10-27 Added fetch support (norm)
+ *            Removed dependency on aug_* memory routines (norm)
+ *            Added connection pooling support (norm)
+ *            Standardized API routines to pg_* names (norm)
+ * 2006-11-01 Updated pg_insert(), pg_delete(), pg_update() and 
+ *            pg_get_result() to handle failed queries.  Detailed warnings 
+ *            along with the text of the failed query is now displayed in the 
+ *            log. Callers of these routines can now assume that a non-zero 
+ *            rc indicates the query failed and that remedial action may need 
+ *            to be taken. (norm)
+ */
+
+/*! \file
+ *  \brief DB_POSTGRES :: Core
+ *  \ingroup db_postgres
+ *  Module: \ref db_postgres
+ */
+
+/*! maximum number of columns */
+#define MAXCOLUMNS	512
+
+#include <string.h>
+#include <stdio.h>
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "../../db/db.h"
+#include "../../db/db_ut.h"
+#include "../../db/db_query.h"
+#include "dbase.h"
+#include "pg_con.h"
+#include "val.h"
+#include "res.h"
+
+static void db_postgres_free_query(const db_con_t* _con);
+
+
+/*!
+ * \brief Initialize database for future queries
+ * \param _url URL of the database that should be opened
+ * \return database connection on success, NULL on error
+ * \note this function must be called prior to any database functions
+ */
+db_con_t *db_postgres_init(const str* _url)
+{
+	return db_do_init(_url, (void*) db_postgres_new_connection);
+}
+
+
+/*!
+ * \brief Close database when the database is no longer needed
+ * \param _h closed connection, as returned from db_postgres_init
+ * \note free all memory and resources
+ */
+void db_postgres_close(db_con_t* _h)
+{
+	db_do_close(_h, db_postgres_free_connection);
+}
+
+
+/*!
+ * \brief Submit_query, run a query
+ * \param _con database connection
+ * \param _s query string
+ * \return 0 on success, negative on failure
+ */
+static int db_postgres_submit_query(const db_con_t* _con, const str* _s)
+{
+	if(! _con || !_s || !_s->s)
+	{
+		LM_ERR("invalid parameter value\n");
+		return(-1);
+	}
+
+	/* this bit of nonsense in case our connection get screwed up */
+	switch(PQstatus(CON_CONNECTION(_con)))
+	{
+		case CONNECTION_OK: 
+			break;
+		case CONNECTION_BAD:
+			LM_DBG("connection reset\n");
+			PQreset(CON_CONNECTION(_con));
+			break;
+		case CONNECTION_STARTED:
+		case CONNECTION_MADE:
+		case CONNECTION_AWAITING_RESPONSE:
+		case CONNECTION_AUTH_OK:
+		case CONNECTION_SETENV:
+		case CONNECTION_SSL_STARTUP:
+		case CONNECTION_NEEDED:
+		default:
+			LM_ERR("%p PQstatus(%s) invalid: %.*s\n", _con,
+				PQerrorMessage(CON_CONNECTION(_con)), _s->len, _s->s);
+			return -1;
+	}
+
+	/* free any previous query that is laying about */
+	db_postgres_free_query(_con);
+
+	/* exec the query */
+	if (PQsendQuery(CON_CONNECTION(_con), _s->s)) {
+		LM_DBG("%p PQsendQuery(%.*s)\n", _con, _s->len, _s->s);
+	} else {
+		LM_ERR("%p PQsendQuery Error: %s Query: %.*s\n", _con,
+		PQerrorMessage(CON_CONNECTION(_con)), _s->len, _s->s);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*!
+ * \brief Gets a partial result set, fetch rows from a result
+ *
+ * Gets a partial result set, fetch a number of rows from a database result.
+ * This function initialize the given result structure on the first run, and
+ * fetches the nrows number of rows. On subsequenting runs, it uses the
+ * existing result and fetches more rows, until it reaches the end of the
+ * result set. Because of this the result needs to be null in the first
+ * invocation of the function. If the number of wanted rows is zero, the
+ * function returns anything with a result of zero.
+ * \param _con database connection
+ * \param _res result set
+ * \param nrows number of fetches rows
+ * \return 0 on success, negative on failure
+ */
+int db_postgres_fetch_result(const db_con_t* _con, db_res_t** _res, const int nrows)
+{
+	int rows;
+	PGresult *res = NULL;
+	ExecStatusType pqresult;
+
+	if (!_con || !_res || nrows < 0) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	/* exit if the fetch count is zero */
+	if (nrows == 0) {
+		if (*_res)
+			db_free_result(*_res);
+
+		*_res = 0;
+		return 0;
+	}
+
+	if (*_res == NULL) {
+		/* Allocate a new result structure */
+		*_res = db_new_result();
+
+		/* Get the result of the previous query */
+		while (1) {
+			if ((res = PQgetResult(CON_CONNECTION(_con)))) {
+				CON_RESULT(_con) = res;
+			} else {
+				break;
+			}
+		}
+		pqresult = PQresultStatus(CON_RESULT(_con));
+		LM_DBG("%p PQresultStatus(%s) PQgetResult(%p)\n", _con,
+			PQresStatus(pqresult), CON_RESULT(_con));
+
+		switch(pqresult) {
+			case PGRES_COMMAND_OK:
+				/* Successful completion of a command returning no data (such as INSERT or UPDATE). */
+				return 0;
+
+			case PGRES_TUPLES_OK:
+				/* Successful completion of a command returning data (such as a SELECT or SHOW). */
+				if (db_postgres_get_columns(_con, *_res) < 0) {
+					LM_ERR("failed to get column names\n");
+					return -2;
+				}
+				break;
+
+			case PGRES_FATAL_ERROR:
+				LM_ERR("%p - invalid query, execution aborted\n", _con);
+				LM_ERR("%p - PQresultStatus(%s)\n", _con, PQresStatus(pqresult));
+				LM_ERR("%p: %s\n", _con, PQresultErrorMessage(CON_RESULT(_con)));
+				if (*_res)
+					db_free_result(*_res);
+				*_res = 0;
+				return -3;
+
+			case PGRES_EMPTY_QUERY:
+			/* notice or warning */
+			case PGRES_NONFATAL_ERROR:
+			/* status for COPY command, not used */
+			case PGRES_COPY_OUT:
+			case PGRES_COPY_IN:
+			/* unexpected response */
+			case PGRES_BAD_RESPONSE:
+			default:
+				LM_ERR("%p - probable invalid query\n", _con);
+				LM_ERR("%p - PQresultStatus(%s)\n", _con, PQresStatus(pqresult));
+				LM_ERR("%p: %s\n", _con, PQresultErrorMessage(CON_RESULT(_con)));
+				if (*_res)
+					db_free_result(*_res);
+				*_res = 0;
+				return -4;
+		}
+
+	} else {
+		if(RES_ROWS(*_res) != NULL) {
+			db_free_rows(*_res);
+		}
+		RES_ROWS(*_res) = 0;
+		RES_ROW_N(*_res) = 0;
+	}
+
+	/* Get the number of rows (tuples) in the query result. */
+	RES_NUM_ROWS(*_res) = PQntuples(CON_RESULT(_con));
+
+	/* determine the number of rows remaining to be processed */
+	rows = RES_NUM_ROWS(*_res) - RES_LAST_ROW(*_res);
+
+	/* If there aren't any more rows left to process, exit */
+	if (rows <= 0)
+		return 0;
+
+	/* if the fetch count is less than the remaining rows to process                 */
+	/* set the number of rows to process (during this call) equal to the fetch count */
+	if (nrows < rows)
+		rows = nrows;
+
+	RES_ROW_N(*_res) = rows;
+
+	LM_DBG("converting row %d of %d count %d\n", RES_LAST_ROW(*_res),
+			RES_NUM_ROWS(*_res), RES_ROW_N(*_res));
+
+	if (db_postgres_convert_rows(_con, *_res) < 0) {
+		LM_ERR("failed to convert rows\n");
+		if (*_res)
+			db_free_result(*_res);
+
+		*_res = 0;
+		return -3;
+	}
+
+	/* update the total number of rows processed */
+	RES_LAST_ROW(*_res) += rows;
+	return 0;
+}
+
+
+/*!
+ * \brief Free database and any old query results
+ * \param _con database connection
+ */
+static void db_postgres_free_query(const db_con_t* _con)
+{
+	if(CON_RESULT(_con))
+	{
+		LM_DBG("PQclear(%p) result set\n", CON_RESULT(_con));
+		PQclear(CON_RESULT(_con));
+		CON_RESULT(_con) = 0;
+	}
+}
+
+
+/*!
+ * \brief Free the query and the result memory in the core
+ * \param _con database connection
+ * \param _r result set
+ * \return 0 on success, -1 on failure
+ */
+int db_postgres_free_result(db_con_t* _con, db_res_t* _r)
+{
+     if ((!_con) || (!_r)) {
+	     LM_ERR("invalid parameter value\n");
+	     return -1;
+     }
+     if (db_free_result(_r) < 0) {
+	     LM_ERR("unable to free result structure\n");
+	     return -1;
+     }
+	db_postgres_free_query(_con);
+	return 0;
+}
+
+
+/*!
+ * \brief Query table for specified rows
+ * \param _h structure representing database connection
+ * \param _k key names
+ * \param _op operators
+ * \param _v values of the keys that must match
+ * \param _c column names to return
+ * \param _n nmber of key=values pairs to compare
+ * \param _nc number of columns to return
+ * \param _o order by the specified column
+ * \param _r result set
+ * \return 0 on success, negative on failure
+ */
+int db_postgres_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op,
+	     const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,
+	     const db_key_t _o, db_res_t** _r)
+{
+	return db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, _r, db_postgres_val2str,
+		db_postgres_submit_query, db_postgres_store_result);
+}
+
+
+/*!
+ * Execute a raw SQL query
+ * \param _h database connection
+ * \param _s raw query string
+ * \param _r result set
+ * \return 0 on success, negative on failure
+ */
+int db_postgres_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r)
+{
+	return db_do_raw_query(_h, _s, _r, db_postgres_submit_query,
+		db_postgres_store_result);
+}
+
+
+/*!
+ * \brief Retrieve result set
+ * \param _con structure representing the database connection
+ * \param _r pointer to a structure represending the result set
+ * \return 0 If the status of the last command produced a result set and,
+ *   If the result set contains data or the convert_result() routine
+ *   completed successfully. Negative if the status of the last command was
+ * not handled or if the convert_result() returned an error.
+ * \note A new result structure is allocated on every call to this routine.
+ * If this routine returns 0, it is the callers responsbility to free the
+ * result structure. If this routine returns < 0, then the result structure
+ * is freed before returning to the caller.
+ */
+int db_postgres_store_result(const db_con_t* _con, db_res_t** _r)
+{
+	PGresult *res = NULL;
+	ExecStatusType pqresult;
+	int rc = 0;
+
+	*_r = db_new_result();
+	if (*_r==NULL) {
+		LM_ERR("failed to init new result\n");
+		rc = -1;
+		goto done;
+	}
+
+	while (1) {
+		if ((res = PQgetResult(CON_CONNECTION(_con)))) {
+			CON_RESULT(_con) = res;
+		} else {
+			break;
+		}
+	}
+
+	pqresult = PQresultStatus(CON_RESULT(_con));
+	
+	LM_DBG("%p PQresultStatus(%s) PQgetResult(%p)\n", _con,
+		PQresStatus(pqresult), CON_RESULT(_con));
+
+	switch(pqresult) {
+		case PGRES_COMMAND_OK:
+		/* Successful completion of a command returning no data
+		 * (such as INSERT or UPDATE). */
+		rc = 0;
+		break;
+
+		case PGRES_TUPLES_OK:
+			/* Successful completion of a command returning data
+			 * (such as a SELECT or SHOW). */
+			if (db_postgres_convert_result(_con, *_r) < 0) {
+				LM_ERR("error while converting result\n");
+				LM_DBG("freeing result set at %p\n", _r);
+				pkg_free(*_r);
+				*_r = 0;
+				rc = -4;
+				break;
+			}
+			rc =  0;
+			break;
+		/* query failed */
+		case PGRES_FATAL_ERROR:
+			LM_ERR("invalid query, execution aborted\n");
+			LM_ERR("driver error: %s, %s\n", PQresStatus(pqresult), PQresultErrorMessage(CON_RESULT(_con)));
+			db_free_result(*_r);
+			*_r = 0;
+			rc = -3;
+			break;
+
+		case PGRES_EMPTY_QUERY:
+		/* notice or warning */
+		case PGRES_NONFATAL_ERROR:
+		/* status for COPY command, not used */
+		case PGRES_COPY_OUT:
+		case PGRES_COPY_IN:
+		/* unexpected response */
+		case PGRES_BAD_RESPONSE:
+		default:
+			LM_ERR("probable invalid query, execution aborted\n");
+			LM_ERR("driver message: %s, %s\n", PQresStatus(pqresult), PQresultErrorMessage(CON_RESULT(_con)));
+			db_free_result(*_r);
+			*_r = 0;
+			rc = -4;
+			break;
+	}
+
+done:
+	db_postgres_free_query(_con);
+	return (rc);
+}
+
+
+/*!
+ * \brief Insert a row into specified table
+ * \param _h structure representing database connection
+ * \param _k key names
+ * \param _v values of the keys
+ * \param _n number of key=value pairs
+ * \return 0 on success, negative on failure
+ */
+int db_postgres_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v,
+		const int _n)
+{
+	db_res_t* _r = NULL;
+
+	int tmp = db_do_insert(_h, _k, _v, _n, db_postgres_val2str, db_postgres_submit_query);
+	// finish the async query, otherwise the next query will not complete
+	if (db_postgres_store_result(_h, &_r) != 0)
+		LM_WARN("unexpected result returned");
+	
+	if (_r)
+		db_free_result(_r);
+
+	return tmp;
+}
+
+
+/*!
+ * \brief Delete a row from the specified table
+ * \param _h structure representing database connection
+ * \param _k key names
+ * \param _o operators
+ * \param _v values of the keys that must match
+ * \param _n number of key=value pairs
+ * \return 0 on success, negative on failure
+ */
+int db_postgres_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+		const db_val_t* _v, const int _n)
+{
+	db_res_t* _r = NULL;
+	int tmp = db_do_delete(_h, _k, _o, _v, _n, db_postgres_val2str,
+		db_postgres_submit_query);
+
+	if (db_postgres_store_result(_h, &_r) != 0)
+		LM_WARN("unexpected result returned");
+	
+	if (_r)
+		db_free_result(_r);
+
+	return tmp;
+}
+
+
+/*!
+ * Update some rows in the specified table
+ * \param _h structure representing database connection
+ * \param _k key names
+ * \param _o operators
+ * \param _v values of the keys that must match
+ * \param _uk updated columns
+ * \param _uv updated values of the columns
+ * \param _n number of key=value pairs
+ * \param _un number of columns to update
+ * \return 0 on success, negative on failure
+ */
+int db_postgres_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+		const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n,
+		const int _un)
+{
+	db_res_t* _r = NULL;
+	int tmp = db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_postgres_val2str,
+		db_postgres_submit_query);
+
+	if (db_postgres_store_result(_h, &_r) != 0)
+		LM_WARN("unexpected result returned");
+	
+	if (_r)
+		db_free_result(_r);
+
+	return tmp;
+}
+
+
+/*!
+ * Store name of table that will be used by subsequent database functions
+ * \param _con database connection
+ * \param _t table name
+ * \return 0 on success, negative on error
+ */
+int db_postgres_use_table(db_con_t* _con, const str* _t)
+{
+	return db_use_table(_con, _t);
+}

+ 113 - 0
modules/db_postgres/km_dbase.h

@@ -0,0 +1,113 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * History
+ * -------
+ * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
+ */
+
+/*! \file
+ *  \brief DB_POSTGRES :: Core
+ *  \ingroup db_postgres
+ *  Module: \ref db_postgres
+ */
+
+#ifndef DBASE_H
+#define DBASE_H
+
+#include "../../db/db_con.h"
+#include "../../db/db_res.h"
+#include "../../db/db_key.h"
+#include "../../db/db_op.h"
+#include "../../db/db_val.h"
+
+
+/*
+ * Initialize database connection
+ */
+db_con_t* db_postgres_init(const str* _url);
+
+/*
+ * Close a database connection
+ */
+void db_postgres_close(db_con_t* _h);
+
+/*
+ * Return result of previous query
+ */
+int db_postgres_store_result(const db_con_t* _h, db_res_t** _r);
+
+
+/*
+ * Free all memory allocated by get_result
+ */
+int db_postgres_free_result(db_con_t* _h, db_res_t* _r);
+
+
+/*
+ * Do a query
+ */
+int db_postgres_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op,
+		const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,
+		const db_key_t _o, db_res_t** _r);
+
+/*
+ * Raw SQL query
+ */
+int db_postgres_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r);
+
+
+/*
+ * Insert a row into table
+ */
+int db_postgres_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v,
+		const int _n);
+
+
+/*
+ * Delete a row from table
+ */
+int db_postgres_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+		const db_val_t* _v, const int _n);
+
+
+/*
+ * Update a row in table
+ */
+int db_postgres_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+		const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n,
+		const int _un);
+
+/*
+ * fetch rows from a result
+ */
+int db_postgres_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows);
+
+
+/*
+ * Store name of table that will be used by
+ * subsequent database functions
+ */
+int db_postgres_use_table(db_con_t* _h, const str* _t);
+
+
+#endif /* DBASE_H */

+ 52 - 0
modules/db_postgres/km_doc/db_postgres.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+
+<!ENTITY admin SYSTEM "db_postgres_admin.xml">
+<!ENTITY faq SYSTEM "../../../doc/module_faq.xml">
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../doc/entities.xml">
+%docentities;
+
+]>
+
+<book>
+    <bookinfo>
+	<title>postgres Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Greg</firstname>
+		<surname>Fausak</surname>
+		<affiliation><orgname>August.net</orgname></affiliation>
+		<address>
+		    <email>[email protected]</email>
+		</address>
+	    </author>
+	    <editor>
+		<firstname>Greg</firstname>
+		<surname>Fausak</surname>
+		<address>
+		    <email>[email protected]</email>
+		</address>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2003</year>
+	    <holder>Greg Fausak</holder>
+	</copyright>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </bookinfo>
+    <toc></toc>
+    
+    &admin;
+    &faq;
+    
+</book>

+ 66 - 0
modules/db_postgres/km_doc/db_postgres_admin.xml

@@ -0,0 +1,66 @@
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>Module description</para>
+	</section>
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>No dependencies on other &kamailio; modules</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>PostgreSQL library</emphasis> - e.g., libpq5.
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+				<emphasis>PostgreSQL devel library</emphasis> - to compile
+				the module (e.g., libpq-dev).
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+
+	<section>
+	<title>Exported Parameters</title>
+		<para>
+		NONE
+		</para>
+	</section>
+
+	<section>
+	<title>Exported Functions</title>
+		<para>
+		NONE
+		</para>
+	</section>
+	<section>
+	<title>Installation and Running</title>
+	<para>Notes about installation and running.</para>
+	</section>
+</chapter>
+

+ 129 - 0
modules/db_postgres/km_pg_con.c

@@ -0,0 +1,129 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2001-2004 iptel.org
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ */
+
+/*! \file
+ *  \brief DB_POSTGRES :: Core
+ *  \ingroup db_postgres
+ *  Module: \ref db_postgres
+ */
+
+#include "pg_con.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include <string.h>
+#include <time.h>
+
+
+/*!
+ * \brief Create a new connection
+ *
+ * Create a new connection structure in private memory, open the PostgreSQL
+ * connection and set reference count to 1
+ * \param id database id
+ * \return postgres connection structure, 0 on error
+ */
+struct pg_con* db_postgres_new_connection(struct db_id* id)
+{
+	struct pg_con* ptr;
+	char *ports;
+
+	LM_DBG("db_id = %p\n", id);
+ 
+	if (!id) {
+		LM_ERR("invalid db_id parameter value\n");
+		return 0;
+	}
+
+	ptr = (struct pg_con*)pkg_malloc(sizeof(struct pg_con));
+	if (!ptr) {
+		LM_ERR("failed trying to allocated %lu bytes for connection structure."
+				"\n", (unsigned long)sizeof(struct pg_con));
+		return 0;
+	}
+	LM_DBG("%p=pkg_malloc(%lu)\n", ptr, (unsigned long)sizeof(struct pg_con));
+
+	memset(ptr, 0, sizeof(struct pg_con));
+	ptr->ref = 1;
+
+	if (id->port) {
+		ports = int2str(id->port, 0);
+		LM_DBG("opening connection: postgres://xxxx:xxxx@%s:%d/%s\n", ZSW(id->host),
+			id->port, ZSW(id->database));
+	} else {
+		ports = NULL;
+		LM_DBG("opening connection: postgres://xxxx:xxxx@%s/%s\n", ZSW(id->host),
+			ZSW(id->database));
+	}
+
+ 	ptr->con = PQsetdbLogin(id->host, ports, NULL, NULL, id->database, id->username, id->password);
+	LM_DBG("PQsetdbLogin(%p)\n", ptr->con);
+
+	if( (ptr->con == 0) || (PQstatus(ptr->con) != CONNECTION_OK) )
+	{
+		LM_ERR("%s\n", PQerrorMessage(ptr->con));
+		PQfinish(ptr->con);
+		goto err;
+	}
+
+	ptr->connected = 1;
+	ptr->timestamp = time(0);
+	ptr->id = id;
+
+	return ptr;
+
+ err:
+	if (ptr) {
+		LM_ERR("cleaning up %p=pkg_free()\n", ptr);
+		pkg_free(ptr);
+	}
+	return 0;
+}
+
+
+/*!
+ * \brief Close the connection and release memory
+ * \param con connection
+ */
+void db_postgres_free_connection(struct pool_con* con)
+{
+
+	if (!con) return;
+
+	struct pg_con * _c;
+	_c = (struct pg_con*)con;
+
+	if (_c->res) {
+		LM_DBG("PQclear(%p)\n", _c->res);
+		PQclear(_c->res);
+		_c->res = 0;
+	}
+	if (_c->id) free_db_id(_c->id);
+	if (_c->con) {
+		LM_DBG("PQfinish(%p)\n", _c->con);
+		PQfinish(_c->con);
+		_c->con = 0;
+	}
+	LM_DBG("pkg_free(%p)\n", _c);
+	pkg_free(_c);
+}

+ 78 - 0
modules/db_postgres/km_pg_con.h

@@ -0,0 +1,78 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * History
+ * -------
+ * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
+ */
+
+/*! \file
+ *  \brief DB_POSTGRES :: Core
+ *  \ingroup db_postgres
+ *  Module: \ref db_mysql
+ */
+
+#ifndef PG_CON_H
+#define PG_CON_H
+
+#include "../../db/db_pool.h"
+#include "../../db/db_id.h"
+
+#include <time.h>
+#include <libpq-fe.h>
+
+
+/*! Postgres specific connection data */
+struct pg_con {
+	struct db_id* id;        /*!< Connection identifier */
+	unsigned int ref;        /*!< Reference count */
+	struct pool_con* next;   /*!< Next connection in the pool */
+
+	int connected;      /*!< connection status */
+	char *sqlurl;		/*!< the url we are connected to, all connection memory parents from this */
+	PGconn *con;		/*!< this is the postgres connection */
+	PGresult *res;		/*!< this is the current result */
+	char**  row;		/*!< Actual row in the result */
+	time_t timestamp;	/*!< Timestamp of last query */
+
+};
+
+#define CON_SQLURL(db_con)     (((struct pg_con*)((db_con)->tail))->sqlurl)
+#define CON_RESULT(db_con)     (((struct pg_con*)((db_con)->tail))->res)
+#define CON_CONNECTION(db_con) (((struct pg_con*)((db_con)->tail))->con)
+#define CON_CONNECTED(db_con)  (((struct pg_con*)((db_con)->tail))->connected)
+#define CON_ROW(db_con)	       (((struct pg_con*)((db_con)->tail))->row)
+#define CON_TIMESTAMP(db_con)  (((struct pg_con*)((db_con)->tail))->timestamp)
+#define CON_ID(db_con) 	       (((struct pg_con*)((db_con)->tail))->id)
+
+/*
+ * Create a new connection structure,
+ * open the PostgreSQL connection and set reference count to 1
+ */
+struct pg_con* db_postgres_new_connection(struct db_id* id);
+
+/*
+ * Close the connection and release memory
+ */
+void db_postgres_free_connection(struct pool_con* con);
+
+#endif /* PG_CON_H */

+ 94 - 0
modules/db_postgres/km_pg_type.h

@@ -0,0 +1,94 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * History
+ * -------
+ * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ */
+
+/*
+ * OID definitions, copied from postgresql/catalog/pg_types.h.
+ * It would be probably more correct to use the definitions from there.
+ */
+#define BOOLOID			16
+#define BYTEAOID		17
+#define CHAROID			18
+#define NAMEOID			19
+#define INT8OID			20
+#define INT2OID			21
+#define INT2VECTOROID		22
+#define INT4OID			23
+#define REGPROCOID		24
+#define TEXTOID			25
+#define OIDOID			26
+#define TIDOID			27
+#define XIDOID 			28
+#define CIDOID 			29
+#define OIDVECTOROID		30
+#define POINTOID		600
+#define LSEGOID			601
+#define PATHOID			602
+#define BOXOID			603
+#define POLYGONOID		604
+#define LINEOID			628
+#define FLOAT4OID 		700
+#define FLOAT8OID 		701
+#define ABSTIMEOID		702
+#define RELTIMEOID		703
+#define TINTERVALOID		704
+#define UNKNOWNOID		705
+#define CIRCLEOID		718
+#define CASHOID 		790
+#define MACADDROID 		829
+#define INETOID 		869
+#define CIDROID 		650
+#define ACLITEMOID		1033
+#define BPCHAROID		1042
+#define VARCHAROID		1043
+#define DATEOID			1082
+#define TIMEOID			1083
+#define TIMESTAMPOID		1114
+#define TIMESTAMPTZOID		1184
+#define INTERVALOID		1186
+#define TIMETZOID		1266
+#define BITOID	 		1560
+#define VARBITOID	  	1562
+#define NUMERICOID		1700
+#define REFCURSOROID		1790
+#define REGPROCEDUREOID 	2202
+#define REGOPEROID		2203
+#define REGOPERATOROID		2204
+#define REGCLASSOID		2205
+#define REGTYPEOID		2206
+#define RECORDOID		2249
+#define CSTRINGOID		2275
+#define ANYOID			2276
+#define ANYARRAYOID		2277
+#define VOIDOID			2278
+#define TRIGGEROID		2279
+#define LANGUAGE_HANDLEROID	2280
+#define INTERNALOID		2281
+#define OPAQUEOID		2282

+ 314 - 0
modules/db_postgres/km_res.c

@@ -0,0 +1,314 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Copyright (C) 2006 Norman Brandinger
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * History
+ * -------
+ * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ * 2006-07-26 added BPCHAROID as a valid type for DB_STRING conversions
+ *            this removes the "unknown type 1042" log messages (norm)
+ *
+ * 2006-10-27 Added fetch support (norm)
+ *            Removed dependency on aug_* memory routines (norm)
+ *            Added connection pooling support (norm)
+ *            Standardized API routines to pg_* names (norm)
+ */
+
+/*! \file
+ *  \brief DB_POSTGRES :: Core
+ *  \ingroup db_postgres
+ *  Module: \ref db_postgres
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "../../db/db_id.h"
+#include "../../db/db_res.h"
+#include "../../db/db_con.h"
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "res.h"
+#include "val.h"
+#include "pg_con.h"
+#include "pg_type.h"
+
+
+/*!
+ * \brief Fill the result structure with data from the query
+ * \param _h database connection
+ * \param _r result set
+ * \return 0 on success, negative on error
+ */
+int db_postgres_convert_result(const db_con_t* _h, db_res_t* _r)
+{
+	if (!_h || !_r)  {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	if (db_postgres_get_columns(_h, _r) < 0) {
+		LM_ERR("failed to get column names\n");
+		return -2;
+	}
+
+	if (db_postgres_convert_rows(_h, _r) < 0) {
+		LM_ERR("failed to convert rows\n");
+		db_free_columns(_r);
+		return -3;
+	}
+	return 0;
+}
+
+
+/*!
+ * \brief Get and convert columns from a result set
+ * \param _h database connection
+ * \param _r result set
+ * \return 0 on success, negative on error
+ */
+int db_postgres_get_columns(const db_con_t* _h, db_res_t* _r)
+{
+	int col, datatype;
+
+	if (!_h || !_r)  {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	/* Get the number of rows (tuples) in the query result. */
+	RES_ROW_N(_r) = PQntuples(CON_RESULT(_h));
+
+	/* Get the number of columns (fields) in each row of the query result. */
+	RES_COL_N(_r) = PQnfields(CON_RESULT(_h));
+
+	if (!RES_COL_N(_r)) {
+		LM_DBG("no columns returned from the query\n");
+		return -2;
+	} else {
+		LM_DBG("%d columns returned from the query\n", RES_COL_N(_r));
+	}
+
+	if (db_allocate_columns(_r, RES_COL_N(_r)) != 0) {
+		LM_ERR("could not allocate columns\n");
+		return -3;
+	}
+
+	/* For each column both the name and the OID number of the data type are saved. */
+	for(col = 0; col < RES_COL_N(_r); col++) {
+
+		RES_NAMES(_r)[col] = (str*)pkg_malloc(sizeof(str));
+		if (! RES_NAMES(_r)[col]) {
+			LM_ERR("no private memory left\n");
+			db_free_columns(_r);
+			return -4;
+		}
+		LM_DBG("allocate %d bytes for RES_NAMES[%d] at %p\n", (unsigned int) sizeof(str), col,
+				RES_NAMES(_r)[col]);
+
+		/* The pointer that is here returned is part of the result structure. */
+		RES_NAMES(_r)[col]->s = PQfname(CON_RESULT(_h), col);
+		RES_NAMES(_r)[col]->len = strlen(PQfname(CON_RESULT(_h), col));
+
+		LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_r)[col], col,
+				RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s);
+
+		/* get the datatype of the column */
+		switch(datatype = PQftype(CON_RESULT(_h),col))
+		{
+			case INT2OID:
+			case INT4OID:
+				LM_DBG("use DB_INT result type\n");
+				RES_TYPES(_r)[col] = DB_INT;
+			break;
+
+			case INT8OID:
+				LM_DBG("use DB_BIGINT result type\n");
+				RES_TYPES(_r)[col] = DB_BIGINT;
+
+			case FLOAT4OID:
+			case FLOAT8OID:
+			case NUMERICOID:
+				LM_DBG("use DB_DOUBLE result type\n");
+				RES_TYPES(_r)[col] = DB_DOUBLE;
+			break;
+
+			case DATEOID:
+			case TIMESTAMPOID:
+			case TIMESTAMPTZOID:
+				LM_DBG("use DB_DATETIME result type\n");
+				RES_TYPES(_r)[col] = DB_DATETIME;
+			break;
+
+			case BOOLOID:
+			case CHAROID:
+			case VARCHAROID:
+			case BPCHAROID:
+				LM_DBG("use DB_STRING result type\n");
+				RES_TYPES(_r)[col] = DB_STRING;
+			break;
+
+			case TEXTOID:
+			case BYTEAOID:
+				LM_DBG("use DB_BLOB result type\n");
+				RES_TYPES(_r)[col] = DB_BLOB;
+			break;
+
+			case BITOID:
+			case VARBITOID:
+				LM_DBG("use DB_BITMAP result type\n");
+				RES_TYPES(_r)[col] = DB_BITMAP;
+			break;
+				
+			default:
+				LM_WARN("unhandled data type column (%.*s) type id (%d), "
+						"use DB_STRING as default\n", RES_NAMES(_r)[col]->len,
+						RES_NAMES(_r)[col]->s, datatype);
+				RES_TYPES(_r)[col] = DB_STRING;
+			break;
+		}
+	}
+	return 0;
+}
+
+
+/*!
+ * \brief Convert rows from PostgreSQL to db API representation
+ * \param _h database connection
+ * \param _r result set
+ * \return 0 on success, negative on error
+ */
+int db_postgres_convert_rows(const db_con_t* _h, db_res_t* _r)
+{
+	char **row_buf, *s;
+	int row, col, len;
+
+	if (!_h || !_r)  {
+		LM_ERR("invalid parameter\n");
+		return -1;
+	}
+
+	if (!RES_ROW_N(_r)) {
+		LM_DBG("no rows returned from the query\n");
+		RES_ROWS(_r) = 0;
+		return 0;
+	}
+	/*Allocate an array of pointers per column to holds the string representation */
+	len = sizeof(char *) * RES_COL_N(_r);
+	row_buf = (char**)pkg_malloc(len);
+	if (!row_buf) {
+		LM_ERR("no private memory left\n");
+		return -1;
+	}
+	LM_DBG("allocate for %d columns %d bytes in row buffer at %p\n", RES_COL_N(_r), len, row_buf);
+	memset(row_buf, 0, len);
+
+	if (db_allocate_rows(_r) < 0) {
+		LM_ERR("could not allocate rows\n");
+		LM_DBG("freeing row buffer at %p\n", row_buf);
+		pkg_free(row_buf);
+		return -2;
+	}
+
+	for(row = RES_LAST_ROW(_r); row < (RES_LAST_ROW(_r) + RES_ROW_N(_r)); row++) {
+		for(col = 0; col < RES_COL_N(_r); col++) {
+			/*
+			 * The row data pointer returned by PQgetvalue points to storage
+			 * that is part of the PGresult structure. One should not modify
+			 * the data it points to, and one must explicitly copy the data
+			 * into other storage if it is to be used past the lifetime of
+			 * the PGresult structure itself.
+			 */
+			s = PQgetvalue(CON_RESULT(_h), row, col);
+			LM_DBG("PQgetvalue(%p,%d,%d)=[%s]\n", _h, row, col, s);
+			/*
+			 * A empty string can be a NULL value, or just an empty string.
+			 * This differs from the mysql behaviour, that further processing
+			 * steps expect. So we need to simulate this here unfortunally.
+			 */
+			if (PQgetisnull(CON_RESULT(_h), row, col) == 0) {
+				row_buf[col] = s;
+				LM_DBG("[%d][%d] Column[%.*s]=[%s]\n",
+					row, col, RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s, row_buf[col]);
+			}
+		}
+
+		/* ASSERT: row_buf contains an entire row in strings */
+		if(db_postgres_convert_row(_h, _r, &(RES_ROWS(_r)[row - RES_LAST_ROW(_r)]), row_buf)<0){
+			LM_ERR("failed to convert row #%d\n",  row);
+			RES_ROW_N(_r) = row - RES_LAST_ROW(_r);
+			LM_DBG("freeing row buffer at %p\n", row_buf);
+			pkg_free(row_buf);
+			db_free_rows(_r);
+			return -4;
+		}
+	}
+
+	LM_DBG("freeing row buffer at %p\n", row_buf);
+	pkg_free(row_buf);
+	row_buf = NULL;
+	return 0;
+}
+
+
+/*!
+ * \brief Convert a row from the result query into db API representation
+ * \param _h database connection
+ * \param _r result set
+ * \param _row row
+ * \param row_buf row buffer
+ * \return 0 on success, negative on error
+ */
+int db_postgres_convert_row(const db_con_t* _h, db_res_t* _r, db_row_t* _row,
+		char **row_buf)
+{
+	int col, col_len;
+
+	if (!_h || !_r || !_row)  {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	if (db_allocate_row(_r, _row) != 0) {
+		LM_ERR("could not allocate row\n");
+		return -2;
+	}
+
+	/* For each column in the row */
+	for(col = 0; col < ROW_N(_row); col++) {
+		/* because it can contain NULL */
+		if (!row_buf[col]) {
+			col_len = 0;
+		} else {
+			col_len = strlen(row_buf[col]);
+		}
+		/* Convert the string representation into the value representation */
+		if (db_postgres_str2val(RES_TYPES(_r)[col], &(ROW_VALUES(_row)[col]),
+		row_buf[col], col_len) < 0) {
+			LM_ERR("failed to convert value\n");
+			LM_DBG("free row at %p\n", _row);
+			db_free_row(_row);
+			return -3;
+		}
+	}
+	return 0;
+}

+ 44 - 0
modules/db_postgres/km_res.h

@@ -0,0 +1,44 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ */
+
+/*! \file
+ *  \brief DB_POSTGRES :: Core
+ *  \ingroup db_postgres
+ *  Module: \ref db_postgres
+ */
+
+#ifndef DB_PG_RES_H
+#define DB_PG_RES_H
+
+#include "../../db/db_row.h"
+
+int db_postgres_convert_result(const db_con_t* _h, db_res_t* _r);
+
+int db_postgres_convert_row(const db_con_t* _h, db_res_t* _res, db_row_t* _r,
+	char **row_buf);
+
+int db_postgres_get_columns(const db_con_t* _h, db_res_t* _r);
+
+int db_postgres_convert_rows(const db_con_t* _h, db_res_t* _r);
+
+#endif

+ 200 - 0
modules/db_postgres/km_val.c

@@ -0,0 +1,200 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ *
+ * History
+ * -------
+ * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
+ * 2003-04-14 gmtime changed to localtime because mktime later
+ *            expects localtime, changed daylight saving bug
+ *            previously found in mysql module (janakj)
+ */
+
+/*! \file
+ *  \brief DB_POSTGRES :: Core
+ *  \ingroup db_postgres
+ *  Module: \ref db_postgres
+ */
+
+#include "../../db/db_val.h"
+#include "../../db/db_ut.h"
+#include "../../dprint.h"
+#include "pg_con.h"
+
+#include "../../mem/mem.h"
+#include "val.h"
+
+
+/*!
+ * \brief Convert a str to a db value, copy strings
+ *
+ * Convert a str to a db value, copy strings.
+ * The postgresql module uses a custom escape function for BLOBs.
+ * If the _s is linked in the db_val result, it will be returned zero
+ * \param _t destination value type
+ * \param _v destination value
+ * \param _s source string
+ * \param _l string length
+ * \return 0 on success, negative on error
+ */
+int db_postgres_str2val(const db_type_t _t, db_val_t* _v, const char* _s, const int _l)
+{
+	/* use common function for non BLOB, NULL setting and input parameter checking */
+	if ( _t != DB_BLOB || _s == NULL || _v == NULL) {
+		return db_str2val(_t, _v, _s, _l, 1);
+	} else {
+		char * tmp_s = NULL;
+		LM_DBG("converting BLOB [%.*s]\n", _l, _s);
+		/*
+		 * The string is stored in new allocated memory, which we could
+		 * not free later thus we need to copy it to some new memory here.
+		 */
+ 		tmp_s = (char*)PQunescapeBytea((unsigned char*)_s, (size_t*)(void*)&(VAL_BLOB(_v).len));
+		if(tmp_s==NULL) {
+			LM_ERR("PQunescapeBytea failed\n");
+			return -7;
+		}
+		VAL_BLOB(_v).s = pkg_malloc(VAL_BLOB(_v).len);
+		if (VAL_BLOB(_v).s == NULL) {
+			LM_ERR("no private memory left\n");
+			PQfreemem(tmp_s);
+			return -8;
+		}
+		LM_DBG("allocate %d bytes memory for BLOB at %p", VAL_BLOB(_v).len, VAL_BLOB(_v).s);
+		memcpy(VAL_BLOB(_v).s, tmp_s, VAL_BLOB(_v).len);
+		PQfreemem(tmp_s);
+
+		VAL_TYPE(_v) = DB_BLOB;
+		VAL_FREE(_v) = 1;
+
+		LM_DBG("got blob len %d\n", _l);
+		return 0;
+
+	}
+}
+
+
+/*!
+ * \brief Converting a value to a string
+ *
+ * Converting a value to a string, used when converting result from a query
+ * \param _con database connection
+ * \param _v source value
+ * \param _s target string
+ * \param _len target string length
+ * \return 0 on success, negative on error
+ */
+int db_postgres_val2str(const db_con_t* _con, const db_val_t* _v, char* _s, int* _len)
+{
+	int l, ret, tmp;
+	int pgret;
+	char *tmp_s;
+	size_t tmp_len;
+	char* old_s;
+
+	tmp = db_val2str(_con, _v, _s, _len);
+	if (tmp < 1)
+		return tmp;
+
+	switch(VAL_TYPE(_v)) {
+	case DB_STRING:
+		l = strlen(VAL_STRING(_v));
+		if (*_len < (l * 2 + 3)) {
+			LM_ERR("destination buffer too short for string\n");
+			return -6;
+		} else {
+			old_s = _s;
+			*_s++ = '\'';
+			ret = PQescapeStringConn(CON_CONNECTION(_con), _s, VAL_STRING(_v),
+					l, &pgret);
+			if(pgret!=0)
+			{
+				LM_ERR("PQescapeStringConn failed\n");
+				return -6;
+			}
+			LM_DBG("PQescapeStringConn: in: %d chars,"
+				" out: %d chars\n", l, ret);
+			_s += ret;
+			*_s++ = '\'';
+			*_s = '\0'; /* FIXME */
+			*_len = _s - old_s;
+			return 0;
+		}
+		break;
+
+	case DB_STR:
+		l = VAL_STR(_v).len;
+		if (*_len < (l * 2 + 3)) {
+			LM_ERR("destination buffer too short for str\n");
+			return -7;
+		} else {
+			old_s = _s;
+			*_s++ = '\'';
+			ret = PQescapeStringConn(CON_CONNECTION(_con), _s, VAL_STRING(_v),
+					l, &pgret);
+			if(pgret!=0)
+			{
+				LM_ERR("PQescapeStringConn failed \n");
+				return -7;
+			}
+	        LM_DBG("PQescapeStringConn: in: %d chars, out: %d chars\n", l, ret);
+			_s += ret;
+			*_s++ = '\'';
+			*_s = '\0'; /* FIXME */
+			*_len = _s - old_s;
+			return 0;
+		}
+		break;
+
+	case DB_BLOB:
+		l = VAL_BLOB(_v).len;
+		/* this estimation is not always correct, thus we need to check later again */
+		if (*_len < (l * 2 + 3)) {
+			LM_ERR("destination buffer too short for blob\n");
+			return -9;
+		} else {
+			*_s++ = '\'';
+			tmp_s = (char*)PQescapeByteaConn(CON_CONNECTION(_con), (unsigned char*)VAL_STRING(_v),
+					(size_t)l, (size_t*)&tmp_len);
+			if(tmp_s==NULL)
+			{
+				LM_ERR("PQescapeByteaConn failed\n");
+				return -9;
+			}
+			if (tmp_len > *_len) {
+				LM_ERR("escaped result too long\n");
+				return -9;
+			}
+			memcpy(_s, tmp_s, tmp_len);
+			PQfreemem(tmp_s);
+			tmp_len = strlen(_s);
+			*(_s + tmp_len) = '\'';
+			*(_s + tmp_len + 1) = '\0';
+			*_len = tmp_len + 2;
+			return 0;
+		}
+		break;
+
+	default:
+		LM_DBG("unknown data type\n");
+		return -10;
+	}
+}

+ 36 - 0
modules/db_postgres/km_val.h

@@ -0,0 +1,36 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ */
+
+/*! \file
+ *  \brief DB_POSTGRES :: Core
+ *  \ingroup db_postgres
+ *  Module: \ref db_postgres
+ */
+
+#ifndef DB_PG_VAL_H
+#define DB_PG_VAL_H
+
+int db_postgres_str2val(const db_type_t _t, db_val_t* _v, const char* _s, const int _l);
+
+int db_postgres_val2str(const db_con_t* _con, const db_val_t* _v, char* _s, int* _len);
+
+#endif