Преглед на файлове

Merge branch 'master' of ssh://[email protected]/sip-router into flatstore

* 'master' of ssh://[email protected]/sip-router: (119 commits)
  Integrate functions and parameters from kamailio sources.
  Ignore emacs backup and lock files.
  Various changes to get rid of conflicts and make km_ files compile.
  Fixed matching of db flags names at the beginning of line.
  Two small fixes in regular expressions.
  Module renamed to db_postgres.
  Merged contents of km_Makefile (namely pg_config autodetection).
  Removed km_Makefile and km_README.
  Updated filenames of locally includede header files
  Align defines protecting headers with filenames.
  Link with both srdb1 and srdb2
  SER to sip-router module conversion script.
  Kamailio to sip-router module conversion script.
  Remove compiler warning about uninitialized variable.
  Make the module compile in the sip-router tree.
  - 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
  ...
Jan Janak преди 16 години
родител
ревизия
ffb2f540c9
променени са 34 файла, в които са добавени 6330 реда и са изтрити 1 реда
  1. 4 1
      .gitignore
  2. 37 0
      modules/db_postgres/Makefile
  3. 134 0
      modules/db_postgres/README
  4. 97 0
      modules/db_postgres/km_db_postgres.c
  5. 39 0
      modules/db_postgres/km_db_postgres.h
  6. 547 0
      modules/db_postgres/km_dbase.c
  7. 113 0
      modules/db_postgres/km_dbase.h
  8. 52 0
      modules/db_postgres/km_doc/db_postgres.xml
  9. 66 0
      modules/db_postgres/km_doc/db_postgres_admin.xml
  10. 129 0
      modules/db_postgres/km_pg_con.c
  11. 78 0
      modules/db_postgres/km_pg_con.h
  12. 94 0
      modules/db_postgres/km_pg_type.h
  13. 314 0
      modules/db_postgres/km_res.c
  14. 44 0
      modules/db_postgres/km_res.h
  15. 200 0
      modules/db_postgres/km_val.c
  16. 36 0
      modules/db_postgres/km_val.h
  17. 516 0
      modules/db_postgres/pg_cmd.c
  18. 155 0
      modules/db_postgres/pg_cmd.h
  19. 326 0
      modules/db_postgres/pg_con.c
  20. 104 0
      modules/db_postgres/pg_con.h
  21. 933 0
      modules/db_postgres/pg_fld.c
  22. 148 0
      modules/db_postgres/pg_fld.h
  23. 546 0
      modules/db_postgres/pg_mod.c
  24. 47 0
      modules/db_postgres/pg_mod.h
  25. 184 0
      modules/db_postgres/pg_oid.c
  26. 128 0
      modules/db_postgres/pg_oid.h
  27. 76 0
      modules/db_postgres/pg_res.c
  28. 58 0
      modules/db_postgres/pg_res.h
  29. 424 0
      modules/db_postgres/pg_sql.c
  30. 118 0
      modules/db_postgres/pg_sql.h
  31. 298 0
      modules/db_postgres/pg_uri.c
  32. 71 0
      modules/db_postgres/pg_uri.h
  33. 107 0
      scripts/kam_to_sr.sh
  34. 107 0
      scripts/ser_to_sr.sh

+ 4 - 1
.gitignore

@@ -26,4 +26,7 @@ TAGS
 test/udp_flood
 utils/gen_ha1/gen_ha1
 utils/sercmd/sercmd
-*~
+# Emacs backup files
+*~
+# Emacs file locks
+.#*

+ 37 - 0
modules/db_postgres/Makefile

@@ -0,0 +1,37 @@
+# $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 \
+		 -L /usr/lib -lpq
+endif
+
+#DEFS += -DPG_TEST
+DEFS += -DSER_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS=$(SERLIBPATH)/srdb2/srdb2 $(SERLIBPATH)/srdb1/srdb1
+
+include ../../Makefile.modules

+ 134 - 0
modules/db_postgres/README

@@ -0,0 +1,134 @@
+
+postgres Module
+
+Greg Fausak
+
+   August.net
+
+Edited by
+
+Greg Fausak
+
+   Copyright © 2003 Greg Fausak
+     _________________________________________________________
+
+   Table of Contents
+   1. User's Guide
+
+        1.1. Overview
+        1.2. Dependencies
+
+              1.2.1. SER Modules
+              1.2.2. External Libraries or Applications
+
+        1.3. Exported Parameters
+
+              1.3.1. param_name (param_type)
+
+        1.4. Exported Functions
+
+              1.4.1. function_name(param1, param2)
+
+        1.5. Installation & Running
+
+   2. Developer's Guide
+   3. Frequently Asked Questions
+
+   List of Examples
+   1-1. Set param_name parameter
+   1-2. function_name usage
+     _________________________________________________________
+
+Chapter 1. User's Guide
+
+1.1. Overview
+
+   Module description
+     _________________________________________________________
+
+1.2. Dependencies
+
+1.2.1. SER Modules
+
+   The following modules must be loaded before this module:
+
+     * No dependencies on other SER modules.
+     _________________________________________________________
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed
+   before running SER with this module loaded:
+
+     * None.
+     _________________________________________________________
+
+1.3. Exported Parameters
+
+1.3.1. param_name (param_type)
+
+   Param description.
+
+   Default value is "value". 
+
+   Example 1-1. Set param_name parameter
+...
+modparam("module", "param_name", "param_value")
+...
+     _________________________________________________________
+
+1.4. Exported Functions
+
+1.4.1. function_name(param1, param2)
+
+   Description
+
+   Meaning of the parameters is as follows:
+
+     * param1 - description.
+     * param2 - description.
+
+   Example 1-2. function_name usage
+...
+function_name("sample_param1", "sample_param2");
+...
+     _________________________________________________________
+
+1.5. Installation & Running
+
+   Notes about installation and running.
+     _________________________________________________________
+
+Chapter 2. Developer's Guide
+
+   The module does not provide any sort of API to use in other
+   SER modules.
+     _________________________________________________________
+
+Chapter 3. Frequently Asked Questions
+
+   3.1. Where can I find more about SER?
+   3.2. Where can I post a question about this module?
+   3.3. How can I report a bug?
+
+   3.1. Where can I find more about SER?
+
+   Take a look at http://iptel.org/ser.
+
+   3.2. Where can I post a question about this module?
+
+   First at all check if your question was already answered on
+   one of our mailing lists:
+
+     * http://mail.iptel.org/mailman/listinfo/serusers
+     * http://mail.iptel.org/mailman/listinfo/serdev
+
+   E-mails regarding any stable version should be sent to
+   <[email protected]> and e-mail regarding development versions
+   or CVS snapshots should be send to <[email protected]>.
+
+
+   3.3. How can I report a bug?
+
+   Please follow the guidelines provided at:
+   http://iptel.org/ser/bugs

+ 97 - 0
modules/db_postgres/km_db_postgres.c

@@ -0,0 +1,97 @@
+/*
+ * $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 "../../lib/srdb1/db_con.h"
+#include "../../lib/srdb1/db.h"
+#include "km_dbase.h"
+#include "km_db_postgres.h"
+
+
+/*MODULE_VERSION*/
+
+/*
+ * PostgreSQL database module interface
+ */
+
+static kam_cmd_export_t cmds[]={
+	{"db_bind_api",     (cmd_function)db_postgres_bind_api,     0, 0, 0, 0},
+	{0,0,0,0,0,0}
+};
+
+
+
+struct kam_module_exports kam_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 */
+	km_postgres_mod_init, /* module initialization function */
+	0,        /* response function*/
+	0,        /* destroy function */
+	0         /* per-child init function */
+};
+
+
+int km_postgres_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;
+}

+ 39 - 0
modules/db_postgres/km_db_postgres.h

@@ -0,0 +1,39 @@
+/*
+ * $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
+ */
+
+#ifndef _KM_DB_POSTGRES_H
+#define _KM_DB_POSTGRES_H
+
+#include "../../lib/srdb1/db.h"
+
+int db_postgres_bind_api(db_func_t *dbb);
+
+int km_postgres_mod_init(void);
+
+#endif /* _KM_DB_POSTGRES_H */

+ 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 "../../lib/srdb1/db.h"
+#include "../../lib/srdb1/db_ut.h"
+#include "../../lib/srdb1/db_query.h"
+#include "km_dbase.h"
+#include "km_pg_con.h"
+#include "km_val.h"
+#include "km_res.h"
+
+static void db_postgres_free_query(const db1_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
+ */
+db1_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(db1_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 db1_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 db1_con_t* _con, db1_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 db1_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(db1_con_t* _con, db1_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 db1_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, db1_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 db1_con_t* _h, const str* _s, db1_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 db1_con_t* _con, db1_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 db1_con_t* _h, const db_key_t* _k, const db_val_t* _v,
+		const int _n)
+{
+	db1_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 db1_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+		const db_val_t* _v, const int _n)
+{
+	db1_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 db1_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)
+{
+	db1_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(db1_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 KM_DBASE_H
+#define KM_DBASE_H
+
+#include "../../lib/srdb1/db_con.h"
+#include "../../lib/srdb1/db_res.h"
+#include "../../lib/srdb1/db_key.h"
+#include "../../lib/srdb1/db_op.h"
+#include "../../lib/srdb1/db_val.h"
+
+
+/*
+ * Initialize database connection
+ */
+db1_con_t* db_postgres_init(const str* _url);
+
+/*
+ * Close a database connection
+ */
+void db_postgres_close(db1_con_t* _h);
+
+/*
+ * Return result of previous query
+ */
+int db_postgres_store_result(const db1_con_t* _h, db1_res_t** _r);
+
+
+/*
+ * Free all memory allocated by get_result
+ */
+int db_postgres_free_result(db1_con_t* _h, db1_res_t* _r);
+
+
+/*
+ * Do a query
+ */
+int db_postgres_query(const db1_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, db1_res_t** _r);
+
+/*
+ * Raw SQL query
+ */
+int db_postgres_raw_query(const db1_con_t* _h, const str* _s, db1_res_t** _r);
+
+
+/*
+ * Insert a row into table
+ */
+int db_postgres_insert(const db1_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 db1_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 db1_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 db1_con_t* _h, db1_res_t** _r, const int nrows);
+
+
+/*
+ * Store name of table that will be used by
+ * subsequent database functions
+ */
+int db_postgres_use_table(db1_con_t* _h, const str* _t);
+
+
+#endif /* KM_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 "km_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 KM_PG_CON_H
+#define KM_PG_CON_H
+
+#include "../../lib/srdb1/db_pool.h"
+#include "../../lib/srdb1/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 /* KM_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 DB1_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 "../../lib/srdb1/db_id.h"
+#include "../../lib/srdb1/db_res.h"
+#include "../../lib/srdb1/db_con.h"
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "km_res.h"
+#include "km_val.h"
+#include "km_pg_con.h"
+#include "km_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 db1_con_t* _h, db1_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 db1_con_t* _h, db1_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 DB1_INT result type\n");
+				RES_TYPES(_r)[col] = DB1_INT;
+			break;
+
+			case INT8OID:
+				LM_DBG("use DB1_BIGINT result type\n");
+				RES_TYPES(_r)[col] = DB1_BIGINT;
+
+			case FLOAT4OID:
+			case FLOAT8OID:
+			case NUMERICOID:
+				LM_DBG("use DB1_DOUBLE result type\n");
+				RES_TYPES(_r)[col] = DB1_DOUBLE;
+			break;
+
+			case DATEOID:
+			case TIMESTAMPOID:
+			case TIMESTAMPTZOID:
+				LM_DBG("use DB1_DATETIME result type\n");
+				RES_TYPES(_r)[col] = DB1_DATETIME;
+			break;
+
+			case BOOLOID:
+			case CHAROID:
+			case VARCHAROID:
+			case BPCHAROID:
+				LM_DBG("use DB1_STRING result type\n");
+				RES_TYPES(_r)[col] = DB1_STRING;
+			break;
+
+			case TEXTOID:
+			case BYTEAOID:
+				LM_DBG("use DB1_BLOB result type\n");
+				RES_TYPES(_r)[col] = DB1_BLOB;
+			break;
+
+			case BITOID:
+			case VARBITOID:
+				LM_DBG("use DB1_BITMAP result type\n");
+				RES_TYPES(_r)[col] = DB1_BITMAP;
+			break;
+				
+			default:
+				LM_WARN("unhandled data type column (%.*s) type id (%d), "
+						"use DB1_STRING as default\n", RES_NAMES(_r)[col]->len,
+						RES_NAMES(_r)[col]->s, datatype);
+				RES_TYPES(_r)[col] = DB1_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 db1_con_t* _h, db1_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 db1_con_t* _h, db1_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 KM_RES_H
+#define KM_RES_H
+
+#include "../../lib/srdb1/db_row.h"
+
+int db_postgres_convert_result(const db1_con_t* _h, db1_res_t* _r);
+
+int db_postgres_convert_row(const db1_con_t* _h, db1_res_t* _res, db_row_t* _r,
+	char **row_buf);
+
+int db_postgres_get_columns(const db1_con_t* _h, db1_res_t* _r);
+
+int db_postgres_convert_rows(const db1_con_t* _h, db1_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 "../../lib/srdb1/db_val.h"
+#include "../../lib/srdb1/db_ut.h"
+#include "../../dprint.h"
+#include "km_pg_con.h"
+
+#include "../../mem/mem.h"
+#include "km_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 != DB1_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) = DB1_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 db1_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 DB1_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 DB1_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 DB1_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 KM_VAL_H
+#define KM_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 db1_con_t* _con, const db_val_t* _v, char* _s, int* _len);
+
+#endif

+ 516 - 0
modules/db_postgres/pg_cmd.c

@@ -0,0 +1,516 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file
+ * Implementation of functions related to database commands.
+ */
+
+#include "pg_cmd.h"
+#include "pg_sql.h"
+#include "pg_fld.h"
+#include "pg_con.h"
+#include "pg_mod.h"
+#include "pg_uri.h"
+#include "pg_res.h"
+
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+
+#include <string.h>
+
+/** A global counter used to generate unique command names.
+ * This variable implements a global counter which is used to generate unique
+ * names for server-side commands.
+ */
+static int server_query_no = 0;
+
+static int upload_cmd(db_cmd_t* cmd);
+static void free_pg_params(struct pg_params* cmd);
+
+
+/** Destroys a pg_cmd structure.
+ * This function frees all memory used by pg_cmd structure.
+ * @param cmd A pointer to generic db_cmd command being freed.
+ * @param payload A pointer to pg_cmd structure to be freed.
+ */
+static void pg_cmd_free(db_cmd_t* cmd, struct pg_cmd* payload)
+{
+	db_drv_free(&payload->gen);
+	if (payload->sql_cmd.s) pkg_free(payload->sql_cmd.s);
+	free_pg_params(&payload->params);
+	if (payload->name) pkg_free(payload->name);
+	if (payload->types) PQclear(payload->types);
+	pkg_free(payload);
+}
+
+
+/** Generate a unique name for a server-side PostgreSQL command.
+ * This function generates a unique name for each command that will be used to
+ * identify the prepared statement on the server. The name has only has to be
+ * unique within a connection to the server so we just keep a global counter
+ * and the name will be that number converted to text.
+ *
+ * @param cmd A command whose name is to be generated 
+ * @return A string allocated using pkg_malloc containing the name or NULL on
+ *         error.
+ */
+static int gen_cmd_name(db_cmd_t* cmd)
+{
+	struct pg_cmd* pcmd;
+	char* c;
+	int len;
+
+	pcmd = DB_GET_PAYLOAD(cmd);
+	c = int2str(server_query_no, &len);
+
+	pcmd->name = pkg_malloc(len + 1);
+	if (pcmd->name == NULL) {
+		ERR("postgres: No memory left\n");
+		return -1;
+	}
+	memcpy(pcmd->name, c, len);
+	pcmd->name[len] = '\0';
+	server_query_no++;
+	return 0;
+}
+
+
+/** Creates parameter data structures for PQexecPrepared.
+ * This function creates auxiliary data structures that will be used to pass
+ * parameter value and types to PQexecPrepared.  The function only allocates
+ * memory buffers and determines oids of parameters, actual values will be
+ * assigned by another function at runtime.
+ * @param cmd A command where the data strctures will be created. 
+ * @retval 0 on success.
+ * @retval A negative number on error.
+ */
+static int create_pg_params(db_cmd_t* cmd)
+{
+	int num;
+	struct pg_cmd* pcmd;
+
+	pcmd = DB_GET_PAYLOAD(cmd);
+
+	num = cmd->match_count + cmd->vals_count;
+
+	if (num == 0) return 0;
+	pcmd->params.val = (const char**)pkg_malloc(sizeof(const char*) * num);
+	pcmd->params.len = (int*)pkg_malloc(sizeof(int) * num);
+	pcmd->params.fmt = (int*)pkg_malloc(sizeof(int) * num);
+	
+	if (!pcmd->params.val || 
+		!pcmd->params.len || !pcmd->params.fmt) {
+		ERR("postgres: No memory left\n");
+		goto error;
+	}
+	
+	memset(pcmd->params.val, '\0', sizeof(const char*) * num);
+	memset(pcmd->params.len, '\0', sizeof(int) * num);
+	memset(pcmd->params.fmt, '\0', sizeof(int) * num);
+	pcmd->params.n = num;
+	return 0;
+
+ error:
+	free_pg_params(&pcmd->params);
+	return -1;
+}
+
+
+/**
+ * Free all memory used for PQexecParam parameters. That is
+ * the arrays of Oids, values, lengths, and formats supplied
+ * to PostgreSQL client API functions like PQexecParams.
+ */
+static void free_pg_params(struct pg_params* params)
+{
+	if (params == NULL) return;
+
+	if (params->val) pkg_free(params->val);
+    params->val = NULL;
+
+	if (params->len) pkg_free(params->len);
+	params->len = NULL;
+
+	if (params->fmt) pkg_free(params->fmt);
+	params->fmt = NULL;
+}
+
+
+/** Verify field type compatibility.
+ * This function verifies the types of all parameters of a database command
+ * with the types of corresponding fields on the server to make sure that they
+ * can be converted.
+ * @param cmd A command structure whose parameters are to be checked.
+ * @retval 0 on success.
+ * @retval A negative number if at least one field type does not match.
+ * @todo Store oid and length as part of pg_fld, instead of the arrays used
+ *       as parameters to PQ functions
+ */
+static int check_types(db_cmd_t* cmd) 
+{ 
+	struct pg_cmd* pcmd; 
+	struct pg_con* pcon;
+	
+	pcmd = DB_GET_PAYLOAD(cmd);
+	/* FIXME: The function should take the connection as one of parameters */
+	pcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
+
+	if (pg_check_fld2pg(cmd->match, pcon->oid)) return -1;
+	if (pg_check_fld2pg(cmd->vals, pcon->oid)) return -1;
+	if (pg_check_pg2fld(cmd->result, pcon->oid)) return -1;
+	return 0;
+}
+
+
+static int get_types(db_cmd_t* cmd)
+{
+	struct pg_cmd* pcmd;
+	struct pg_con* pcon;
+
+	pcmd = DB_GET_PAYLOAD(cmd);
+	/* FIXME */
+	pcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
+
+	pcmd->types = PQdescribePrepared(pcon->con, pcmd->name);
+	
+	if (PQresultStatus(pcmd->types) != PGRES_COMMAND_OK) {
+		ERR("postgres: Error while obtaining description of prepared statement\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int pg_cmd(db_cmd_t* cmd)
+{
+	struct pg_cmd* pcmd;
+ 
+	pcmd = (struct pg_cmd*)pkg_malloc(sizeof(struct pg_cmd));
+	if (pcmd == NULL) {
+		ERR("postgres: No memory left\n");
+		goto error;
+	}
+	memset(pcmd, '\0', sizeof(struct pg_cmd));
+	if (db_drv_init(&pcmd->gen, pg_cmd_free) < 0) goto error;
+
+	switch(cmd->type) {
+	case DB_PUT:
+		if (build_insert_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
+		break;
+		
+	case DB_DEL:
+		if (build_delete_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
+		break;
+
+	case DB_GET:
+		if (build_select_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
+		break;
+
+	case DB_UPD:
+		if (build_update_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
+		break;
+		
+	case DB_SQL:
+		pcmd->sql_cmd.s = (char*)pkg_malloc(cmd->table.len + 1);
+		if (pcmd->sql_cmd.s == NULL) {
+			ERR("postgres: Out of private memory\n");
+			goto error;
+		}
+		memcpy(pcmd->sql_cmd.s,cmd->table.s, cmd->table.len);
+		pcmd->sql_cmd.s[cmd->table.len] = '\0';
+		pcmd->sql_cmd.len = cmd->table.len;
+        break;
+	}
+
+	DB_SET_PAYLOAD(cmd, pcmd);
+
+	/* Create parameter arrays for PostgreSQL API functions */
+	if (create_pg_params(cmd) < 0) goto error;	
+
+	/* Generate a unique name for the command on the server */
+	if (gen_cmd_name(cmd) != 0) goto error; 
+
+	/* Upload the command to the server */
+	if (upload_cmd(cmd) != 0) goto error;
+
+	/* Obtain the description of the uploaded command, this includes
+	 * information about result and parameter fields */
+	if (get_types(cmd) != 0) goto error;
+
+	/* Update fields based on the information retrieved from the */
+	if (pg_resolve_param_oids(cmd->vals, cmd->match,
+							  cmd->vals_count, cmd->match_count,
+							  pcmd->types)) 
+		goto error;
+	if (pg_resolve_result_oids(cmd->result, cmd->result_count, pcmd->types)) 
+		goto error;
+
+	if (check_types(cmd)) goto error;
+
+	return 0;
+
+ error:
+	if (pcmd) {
+		DB_SET_PAYLOAD(cmd, NULL);
+		free_pg_params(&pcmd->params);
+
+		if (pcmd->types) PQclear(pcmd->types);
+		if (pcmd->name) pkg_free(pcmd->name);
+		if (pcmd->sql_cmd.s) pkg_free(pcmd->sql_cmd.s);
+
+		db_drv_free(&pcmd->gen);
+		pkg_free(pcmd);
+	}
+	return -1;
+}
+
+
+int pg_getopt(db_cmd_t* cmd, char* optname, va_list ap)
+{
+	struct pg_cmd* pcmd;
+	long long* id;
+
+	pcmd = (struct pg_cmd*)DB_GET_PAYLOAD(cmd);
+
+	if (!strcasecmp("last_id", optname)) {
+		id = va_arg(ap, long long*);
+		if (id == NULL) {
+			BUG("postgres: NULL pointer passed to 'last_id' option\n");
+			goto error;
+		}
+		return -1;
+	} else {
+		return 1;
+	}
+	return 0;
+
+ error:
+	return -1;
+}
+
+
+int pg_setopt(db_cmd_t* cmd, char* optname, va_list ap)
+{
+	struct pg_cmd* pcmd;
+
+	pcmd = (struct pg_cmd*)DB_GET_PAYLOAD(cmd);
+	return 1;
+}
+
+
+/** Uploads a database command to PostgreSQL server.
+ * This function uploads a pre-compiled database command to PostgreSQL
+ * server using PQprepare.
+ * @param cmd A database command
+ * @retval 0 on success.
+ * @retval A negative number on error.
+ */
+static int upload_cmd(db_cmd_t* cmd)
+{
+	struct pg_cmd* pcmd;
+	struct pg_con* pcon;
+	PGresult* res;
+	int st;
+
+	pcmd = DB_GET_PAYLOAD(cmd);
+	/* FIXME: The function should take the connection as one of parameters */
+	pcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
+
+	DBG("postgres: Uploading comand '%s': '%s'\n", pcmd->name, 
+		pcmd->sql_cmd.s);
+
+	res = PQprepare(pcon->con, pcmd->name, pcmd->sql_cmd.s, 0, NULL);
+	
+	st = PQresultStatus(res);
+
+	if (st != PGRES_COMMAND_OK && st != PGRES_NONFATAL_ERROR &&
+		st != PGRES_TUPLES_OK) {
+		ERR("postgres: Error while uploading command to server: %d, %s", 
+			st, PQresultErrorMessage(res));
+		ERR("postgres: Command: '%s'\n", pcmd->sql_cmd.s);
+		PQclear(res);
+		return -1;
+	}
+
+	PQclear(res);
+	return 0;
+}
+
+
+int pg_cmd_exec(db_res_t* res, db_cmd_t* cmd)
+{
+	PGresult* tmp;
+	int i, err, stat;
+	db_con_t* con;
+	struct pg_cmd* pcmd;
+	struct pg_con* pcon;
+	struct pg_uri* puri;
+	struct pg_res* pres;
+
+	/* First things first: retrieve connection info from the currently active
+	 * connection and also mysql payload from the database command
+	 */
+	con = cmd->ctx->con[db_payload_idx];
+	pcmd = DB_GET_PAYLOAD(cmd);
+	pcon = DB_GET_PAYLOAD(con);
+	puri = DB_GET_PAYLOAD(con->uri);
+
+	for(i = 0; i <= pg_retries; i++) {
+		/* Convert parameters from DB-API format to the format accepted
+		 * by PostgreSQL */
+		if (pg_fld2pg(&pcmd->params, 0, pcon->oid, cmd->match, pcon->flags) != 0) 
+			return 1;
+
+		if (pg_fld2pg(&pcmd->params, cmd->match_count,
+					  pcon->oid, cmd->vals, pcon->flags) != 0) return 1;
+		
+		/* Execute the statement */
+		tmp = PQexecPrepared(pcon->con, pcmd->name,
+							 pcmd->params.n,
+							 pcmd->params.val, pcmd->params.len,
+							 pcmd->params.fmt, 1);
+		if (!tmp) {
+			ERR("postgres: PQexecPrepared returned no result\n");
+			continue;
+		}
+
+		switch(PQresultStatus(tmp)) {
+		case PGRES_COMMAND_OK:
+		case PGRES_NONFATAL_ERROR:
+		case PGRES_TUPLES_OK:
+			if (res) {
+				pres = DB_GET_PAYLOAD(res);
+				pres->res = tmp;
+				pres->rows = PQntuples(tmp);
+			} else {
+				PQclear(tmp);
+			}
+			return 0;
+			
+		default:
+			break;
+		}
+		ERR("postgres: Command on server %s failed: %s: %s\n", 
+			puri->host, PQresStatus(PQresultStatus(tmp)), 
+			PQresultErrorMessage(tmp));
+		PQclear(tmp);
+
+		/* Command failed, first of all determine the status of the connection
+		 * to server */
+		if (PQstatus(pcon->con) != CONNECTION_OK) {
+			INFO("postgres: Connection to server %s disconnected, attempting reconnect\n", 
+				 puri->host);
+			pg_con_disconnect(con);
+			if (pg_con_connect(con)) {
+				INFO("postgres: Failed to reconnect server %s, giving up\n", 
+					 puri->host);
+				return -1;
+			}
+			INFO("postgres: Successfully reconnected server on %s\n", 
+				 puri->host);
+		}
+
+		/* Connection is either connected or has been successfully reconnected, 
+		 * now figure out if the prepared command on the server still exist
+		 */
+		tmp = PQdescribePrepared(pcon->con, pcmd->name);
+		if (tmp == NULL) {
+			ERR("postgres: PQdescribePrepared returned no result\n");
+		    continue;
+		}
+		stat = PQresultStatus(tmp);
+		PQclear(tmp);
+		switch (stat) {
+		case PGRES_COMMAND_OK:
+		case PGRES_NONFATAL_ERROR:
+		case PGRES_TUPLES_OK:
+			INFO("postgres: Command %s on server %s still exists, reusing\n",
+				 pcmd->name, puri->host);
+			/* Command is there, retry */
+			continue;
+		default:
+			break;
+		}
+
+		/* Upload again */
+		INFO("postgres: Command %s on server %s missing, uploading\n",
+			 pcmd->name, puri->host);
+		err = upload_cmd(cmd);
+		if (err < 0) {
+			continue;
+		} else if (err > 0) {
+			/* DB API error, this is a serious problem such
+			 * as memory allocation failure, bail out
+			 */
+			return 1;
+		}
+	}
+
+	INFO("postgres: Failed to execute command %s on server %s, giving up\n",
+		 pcmd->name, puri->host);
+	return -1;
+}
+
+
+int pg_cmd_first(db_res_t* res)
+{
+	struct pg_res* pres;
+
+	pres = DB_GET_PAYLOAD(res);
+
+	if (pres->rows <= 0) return 1; /* Empty table */
+	pres->row = 0;
+	return pg_cmd_next(res);
+}
+
+
+int pg_cmd_next(db_res_t* res)
+{
+	struct pg_res* pres;
+	struct pg_con* pcon;
+
+	pres = DB_GET_PAYLOAD(res);
+	pcon = DB_GET_PAYLOAD(res->cmd->ctx->con[db_payload_idx]);
+
+	if (pres->row >= pres->rows) return 1;
+
+	if (pg_pg2fld(res->cmd->result, pres->res, pres->row, pcon->oid, pcon->flags)) return -1;
+	res->cur_rec->fld = res->cmd->result;
+	pres->row++;
+	return 0;
+}
+
+/** @} */

+ 155 - 0
modules/db_postgres/pg_cmd.h

@@ -0,0 +1,155 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+#ifndef _PG_CMD_H
+#define _PG_CMD_H
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file 
+ * Declaration of pg_cmd data structure that contains PostgreSQL specific data
+ * stored in db_cmd structures and related functions.
+ */
+
+#include "pg_oid.h"
+
+#include "../../lib/srdb2/db_drv.h"
+#include "../../lib/srdb2/db_cmd.h"
+#include "../../lib/srdb2/db_res.h"
+#include "../../str.h"
+
+#include <stdarg.h>
+#include <libpq-fe.h>
+
+struct pg_params {
+	int n;
+	const char** val;
+	int* len;
+	int* fmt;
+};
+
+
+/** Extension structure of db_cmd adding PostgreSQL specific data.
+ * This data structure extends the generic data structure db_cmd in the
+ * database API with data specific to the postgresql driver.
+ */
+struct pg_cmd {
+	db_drv_t gen; /**< Generic part of the data structure (must be first */
+	char* name;   /**< Name of the prepared query on the server */
+	str sql_cmd;  /**< Database command represented in SQL language */
+
+	struct pg_params params;
+	PGresult* types;
+};
+
+
+/** Creates a new pg_cmd data structure.
+ * This function allocates and initializes memory for a new pg_cmd data
+ * structure. The data structure is then attached to the generic db_cmd
+ * structure in cmd parameter.
+ * @param cmd A generic db_cmd structure to which the newly created pg_cmd
+ *            structure will be attached.
+ */
+int pg_cmd(db_cmd_t* cmd);
+
+
+/** The main execution function in postgres SER driver.
+ * This is the main execution function in this driver. It is executed whenever
+ * a SER module calls db_exec and the target database of the commands is
+ * PostgreSQL. The function contains all the necessary logic to detect reset
+ * or disconnected database connections and uploads commands to the server if
+ * necessary.
+ * @param res A pointer to (optional) result structure if the command returns
+ *            a result.
+ * @retval 0 if executed successfully
+ * @retval A negative number if the database server failed to execute command
+ * @retval A positive number if there was an error on client side (SER)
+ */
+int pg_cmd_exec(db_res_t* res, db_cmd_t* cmd);
+
+
+/** Retrieves the first record from a result set received from PostgreSQL server.
+ * This function is executed whenever a SER module calls db_first to retrieve
+ * the first record from a result. The function retrieves the first record
+ * from a PGresult structure and converts the fields from PostgreSQL to
+ * internal SER representation.
+ * 
+ * @param res A result set retrieved from PostgreSQL server.
+ * @retval 0 If executed successfully.
+ * @retval 1 If the result is empty.
+ * @retival A negative number on error.
+ */
+int pg_cmd_first(db_res_t* res);
+
+
+/** Retrieves the next record from a result set received from PostgreSQL server.
+ * This function is executed whenever a SER module calls db_next to retrieve
+ * the first record from a result. The function advances current cursor
+ * position in the result, retrieves the next record from a PGresult structure
+ * and converts the fields from PostgreSQL to internal SER representation.
+ * 
+ * @param res A result set retrieved from PostgreSQL server.
+ * @retval 0 If executed successfully.
+ * @retval 1 If there are no more records in the result.
+ * @retival A negative number on error.
+ */
+int pg_cmd_next(db_res_t* res);
+
+
+/** Retrieves the value of an db_cmd option.
+ * This function is called when a SER module uses db_getopt to retrieve the
+ * value of db_cmd parameter.
+ * @param cmd A db_cmd structure representing the command.
+ * @param optname Name of the option.
+ * @param ap A pointer the result variable.
+ * @retval 0 on success.
+ * @retval A positive number of the option is not supported/implemented.
+ * @retval A negative number on error.
+ */
+int pg_getopt(db_cmd_t* cmd, char* optname, va_list ap);
+
+
+/** Sets the value of an db_cmd option.
+ * This function is called when a SER module uses db_setopt to set the
+ * value of db_cmd parameter.
+ * @param cmd A db_cmd structure representing the command.
+ * @param optname Name of the option.
+ * @param ap A variable with the value to be set.
+ * @retval 0 on success.
+ * @retval A positive number of the option is not supported/implemented.
+ * @retval A negative number on error.
+ */
+int pg_setopt(db_cmd_t* cmd, char* optname, va_list ap);
+
+/** @} */
+
+#endif /* _PG_CMD_H */

+ 326 - 0
modules/db_postgres/pg_con.c

@@ -0,0 +1,326 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file 
+ * Functions related to connections to PostgreSQL servers.
+ */
+
+#include "pg_con.h"
+#include "pg_uri.h"
+#include "pg_sql.h"
+
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <time.h>
+
+
+/* Override the default notice processor to output the messages 
+ * using SER's output subsystem.
+ */
+static void notice_processor(void* arg, const char* message)
+{
+	LOG(L_NOTICE, "postgres: %s\n", message);
+}
+
+
+/** Determine the format of timestamps used by the server.  
+ * A PostgresSQL server can be configured to store timestamps either as 8-byte
+ * integers or floating point numbers with double precision. This functions
+ * sends a simple SQL query to the server and tries to determine the format of
+ * timestamps from the reply. This function is executed once after connecting
+ * to a PostgreSQL server and the result of the detection is then stored in
+ * form of a flag in pg_con connection structure.
+ * @param con A PostgreSQL connection handle
+ * @retval 0 If the server stores timestamps as floating point numbers.
+ * @retval 1 If the server stores timestamps as 8-byte integers.
+ * @retval A negative number on error.
+ */
+static int timestamp_format(PGconn* con)
+{
+	unsigned long long offset;
+	PGresult* res = 0;
+	char* val;
+	str sql;
+
+	if (build_timestamp_format_sql(&sql) != 0) {
+		ERR("postgres: Error while building SQL query to obtain timestamp format\n");
+		return -1;
+	}
+	res = PQexecParams(con, sql.s, 0, 0, 0, 0, 0, 1);
+	pkg_free(sql.s);
+
+	if (PQfformat(res, 0) != 1) {
+		ERR("postgres: Binary format expected but server sent text\n");
+		goto error;
+	}
+
+	if (PQntuples(res) != 1) {
+		ERR("postgres: Only one column expected, %d received\n", PQntuples(res));
+		goto error;
+	}
+
+	if (PQnfields(res) != 1) {
+		ERR("postgres: Only one row expected, %d received\n", PQnfields(res));
+		goto error;
+	}
+
+	val = PQgetvalue(res, 0, 0);
+	offset = ((unsigned long long)ntohl(((unsigned int*)val)[0]) << 32) 
+		+ ntohl(((unsigned int*)val)[1]);
+	
+	PQclear(res);
+
+	/* Server using int8 timestamps would return 1000000, because it stores
+	 * timestamps in microsecond resolution across the whole range. Server
+	 * using double timestamps would return 1 (encoded as double) here because
+	 * subsection fraction is stored as fractional part in the IEEE
+	 * representation.  1 stored as double would result in 4607182418800017408
+	 * when the memory location occupied by the variable is read as unsigned
+	 * long long.
+	 */
+	if (offset == 1000000) {
+	        DBG("postgres: Server uses int8 format for timestamps.\n");
+		return 1;
+	} else {
+		DBG("postgres: Server uses double format for timestamps.\n");
+		return 0;
+	}
+	
+ error:
+	PQclear(res);
+	return -1;
+}
+
+
+/** Retrieves a list of all supported field types from the server.
+ * This function retrieves a list of all supported field types and their Oids
+ * from system catalogs of the server. The list is then stored in pg_con
+ * connection structure and it is used to map field type names, such as int2,
+ * int4, float4, etc. to Oids. Every PostgreSQL server can map field types to
+ * different Oids so we need to store the mapping array in the connection
+ * structure.
+ * @param con A structure representing connection to PostgreSQL server.
+ * @retval 0 If executed successfully.
+ * @retval A negative number on error.
+ */
+static int get_oids(db_con_t* con)
+{
+	struct pg_con* pcon;
+	PGresult* res = NULL;
+	str sql;
+
+	pcon = DB_GET_PAYLOAD(con);
+	if (build_select_oid_sql(&sql) < 0) goto error;
+	res = PQexec(pcon->con, sql.s);
+	pkg_free(sql.s);
+	if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) goto error;
+	pcon->oid = pg_new_oid_table(res);
+	PQclear(res);
+	if (pcon->oid == NULL) goto error;
+	return 0;
+
+ error:
+	if (res) PQclear(res);
+	return -1;
+}
+
+
+/** Free all memory allocated for a pg_con structure.
+ * This function function frees all memory that is in use by
+ * a pg_con structure.
+ * @param con A generic db_con connection structure.
+ * @param payload PostgreSQL specific payload to be freed.
+ */
+static void pg_con_free(db_con_t* con, struct pg_con* payload)
+{
+	if (!payload) return;
+	
+	/* Delete the structure only if there are no more references
+	 * to it in the connection pool
+	 */
+	if (db_pool_remove((db_pool_entry_t*)payload) == 0) return;
+	
+	db_pool_entry_free(&payload->gen);
+	pg_destroy_oid_table(payload->oid);
+	if (payload->con) PQfinish(payload->con);
+	pkg_free(payload);
+}
+
+
+int pg_con(db_con_t* con)
+{
+	struct pg_con* pcon;
+
+	/* First try to lookup the connection in the connection pool and
+	 * re-use it if a match is found
+	 */
+	pcon = (struct pg_con*)db_pool_get(con->uri);
+	if (pcon) {
+		DBG("postgres: Connection to %.*s:%.*s found in connection pool\n",
+			con->uri->scheme.len, ZSW(con->uri->scheme.s),
+			con->uri->body.len, ZSW(con->uri->body.s));
+		goto found;
+	}
+
+	pcon = (struct pg_con*)pkg_malloc(sizeof(struct pg_con));
+	if (!pcon) {
+		LOG(L_ERR, "postgres: No memory left\n");
+		goto error;
+	}
+	memset(pcon, '\0', sizeof(struct pg_con));
+	if (db_pool_entry_init(&pcon->gen, pg_con_free, con->uri) < 0) goto error;
+
+	DBG("postgres: Preparing new connection to: %.*s:%.*s\n",
+		con->uri->scheme.len, ZSW(con->uri->scheme.s),
+		con->uri->body.len, ZSW(con->uri->body.s));
+
+	/* Put the newly created postgres connection into the pool */
+	db_pool_put((struct db_pool_entry*)pcon);
+	DBG("postgres: Connection stored in connection pool\n");
+
+ found:
+	/* Attach driver payload to the db_con structure and set connect and
+	 * disconnect functions
+	 */
+	DB_SET_PAYLOAD(con, pcon);
+	con->connect = pg_con_connect;
+	con->disconnect = pg_con_disconnect;
+	return 0;
+
+ error:
+	if (pcon) {
+		db_pool_entry_free(&pcon->gen);
+		pkg_free(pcon);
+	}
+	return -1;
+}
+
+
+int pg_con_connect(db_con_t* con)
+{
+	struct pg_con* pcon;
+	struct pg_uri* puri;
+	char* port_str;
+	int ret;
+	
+	pcon = DB_GET_PAYLOAD(con);
+	puri = DB_GET_PAYLOAD(con->uri);
+	
+	/* Do not reconnect already connected connections */
+	if (pcon->flags & PG_CONNECTED) return 0;
+
+	DBG("postgres: Connecting to %.*s:%.*s\n",
+		con->uri->scheme.len, ZSW(con->uri->scheme.s),
+		con->uri->body.len, ZSW(con->uri->body.s));
+
+	if (puri->port > 0) {
+		port_str = int2str(puri->port, 0);
+	} else {
+		port_str = NULL;
+	}
+
+	if (pcon->con) {
+		PQfinish(pcon->con);
+		pcon->con = NULL;
+	}
+
+	pcon->con = PQsetdbLogin(puri->host, port_str,
+							 NULL, NULL, puri->database,
+							 puri->username, puri->password);
+	
+	if (pcon->con == NULL) {
+		ERR("postgres: PQsetdbLogin ran out of memory\n");
+		goto error;
+	}
+	
+	if (PQstatus(pcon->con) != CONNECTION_OK) {
+		ERR("postgres: %s\n", PQerrorMessage(pcon->con));
+		goto error;
+	}
+	
+	/* Override default notice processor */
+	PQsetNoticeProcessor(pcon->con, notice_processor, 0);
+	
+	DBG("postgres: Connected. Protocol version=%d, Server version=%d\n", 
+	    PQprotocolVersion(pcon->con),
+#ifdef HAVE_PGSERVERVERSION
+	    PQserverVersion(pcon->con)
+#else
+	    0
+#endif
+	    );
+
+	ret = timestamp_format(pcon->con);
+	if (ret == 1 || ret == -1) {
+		/* Assume INT8 representation if detection fails */
+		pcon->flags |= PG_INT8_TIMESTAMP;
+	} else {
+		pcon->flags &= ~PG_INT8_TIMESTAMP;
+	}
+
+	if (get_oids(con) < 0) goto error;
+
+	pcon->flags |= PG_CONNECTED;
+	return 0;
+
+ error:
+	if (pcon->con) PQfinish(pcon->con);
+	pcon->con = NULL;
+	return -1;
+}
+
+
+void pg_con_disconnect(db_con_t* con)
+{
+	struct pg_con* pcon;
+
+	pcon = DB_GET_PAYLOAD(con);
+	if ((pcon->flags & PG_CONNECTED) == 0) return;
+
+	DBG("postgres: Disconnecting from %.*s:%.*s\n",
+		con->uri->scheme.len, ZSW(con->uri->scheme.s),
+		con->uri->body.len, ZSW(con->uri->body.s));
+
+	PQfinish(pcon->con);
+	pcon->con = NULL;
+	pcon->flags &= ~PG_CONNECTED;
+	pcon->flags &= ~PG_INT8_TIMESTAMP;
+}
+
+/** @} */

+ 104 - 0
modules/db_postgres/pg_con.h

@@ -0,0 +1,104 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+#ifndef _PG_CON_H
+#define _PG_CON_H
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file 
+ * Implementation of PostgreSQL connection related data structures and functions.
+ */
+
+#include "pg_oid.h"
+
+#include "../../lib/srdb2/db_pool.h"
+#include "../../lib/srdb2/db_con.h"
+#include "../../lib/srdb2/db_uri.h"
+
+#include <time.h>
+#include <libpq-fe.h>
+
+/** 
+ * Per-connection flags for PostgreSQL connections.
+ */
+enum pg_con_flags {
+	PG_CONNECTED      = (1 << 0), /**< The connection has been connected successfully */
+	PG_INT8_TIMESTAMP = (1 << 1)  /**< The server uses 8-byte integer format for timestamps */
+};
+
+
+/** A structure representing a connection to PostgreSQL server.
+ * This structure represents connections to PostgreSQL servers. It contains
+ * PostgreSQL specific data, such as PostgreSQL connection handle, connection
+ * flags, and an array with data types supported by the server.
+ */
+typedef struct pg_con {
+	db_pool_entry_t gen;  /**< Generic part of the structure */
+	PGconn* con;          /**< Postgres connection handle */
+	unsigned int flags;   /**< Flags (currently only binary data format) */
+	pg_type_t* oid;       /**< Data types and their Oids obtained from the server */
+} pg_con_t;
+
+
+/** Create a new pg_con structure.
+ * This function creates a new pg_con structure and attachs the structure to
+ * the generic db_con structure in the parameter.
+ * @param con A generic db_con structure to be extended with PostgreSQL
+ *            payload
+ * @retval 0 on success
+ * @retval A negative number on error
+ */
+int pg_con(db_con_t* con);
+
+
+/** Establish a new connection to server.  
+ * This function is called when a SER module calls db_connect to establish a
+ * new connection to the database server. After the connection is established
+ * the function sends an SQL query to the server to determine the format of
+ * timestamp fields and also obtains the list of supported field types.
+ * @param con A structure representing database connection.
+ * @retval 0 on success.
+ * @retval A negative number on error.
+ */
+int pg_con_connect(db_con_t* con);
+
+
+/** Disconnected from PostgreSQL server.
+ * Disconnects a previously connected connection to PostgreSQL server.
+ * @param con A structure representing the connection to be disconnected.
+ */
+void pg_con_disconnect(db_con_t* con);
+
+/** @} */
+
+#endif /* _PG_CON_H */

+ 933 - 0
modules/db_postgres/pg_fld.c

@@ -0,0 +1,933 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file 
+ * Data field conversion and type checking functions.
+ */
+
+#include "pg_fld.h"
+#include "pg_con.h" /* flags */
+#include "pg_mod.h"
+
+#include "../../lib/srdb2/db_drv.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <string.h>
+
+/**
+ * This is the epoch time in time_t format, this value is used to convert
+ * timestamp values to/from PostgreSQL format.
+ *  2000-01-01 00:00:00 +0000 as the value of time_t in UTC
+ */
+#define PG_EPOCH_TIME ((int64_t)946684800)
+
+
+/** Frees memory used by a pg_fld structure.
+ * This function frees all memory used by a pg_fld structure
+ * @param fld Generic db_fld_t* structure being freed.
+ * @param payload The postgresql extension structure to be freed
+ */
+static void pg_fld_free(db_fld_t* fld, struct pg_fld* payload)
+{
+	db_drv_free(&payload->gen);
+	pkg_free(payload);
+}
+
+
+int pg_fld(db_fld_t* fld, char* table)
+{
+	struct pg_fld* res;
+
+	res = (struct pg_fld*)pkg_malloc(sizeof(struct pg_fld));
+	if (res == NULL) {
+		ERR("postgres: No memory left\n");
+		return -1;
+	}
+	memset(res, '\0', sizeof(struct pg_fld));
+	if (db_drv_init(&res->gen, pg_fld_free) < 0) goto error;
+
+	DB_SET_PAYLOAD(fld, res);
+	return 0;
+
+ error:
+	if (res) pkg_free(res);
+	return -1;
+}
+
+
+static inline uint64_t htonll(uint64_t in)
+{
+	uint32_t* p = (uint32_t*)&in;
+	return ((uint64_t)htonl(p[0]) << 32) + (uint64_t)htonl(p[1]);
+}
+
+
+static inline uint64_t ntohll(uint64_t in)
+{
+	uint32_t* p = (uint32_t*)&in;
+	return ((uint64_t)ntohl(p[0]) << 32) + (uint64_t)ntohl(p[1]);
+}
+
+
+static inline void db_int2pg_int4(struct pg_params* dst, int i, 
+								  db_fld_t* src)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+	pfld->v.int4[0] = htonl(src->v.int4);
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+	dst->len[i] = 4;
+}
+
+
+static inline void db_int2pg_int2(struct pg_params* dst, int i, 
+								  db_fld_t* src)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+	pfld->v.int2[0] = htons(src->v.int4);
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+	dst->len[i] = 2;
+}
+
+
+static inline void db_int2pg_timestamp(struct pg_params* dst, int i, 
+									   db_fld_t* src, unsigned int flags)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+	if (flags & PG_INT8_TIMESTAMP) {
+		pfld->v.int8 = ((int64_t)src->v.int4 - PG_EPOCH_TIME) * 1000000;
+	} else {
+		pfld->v.dbl = (double)src->v.int4 - (double)PG_EPOCH_TIME;
+	}
+	pfld->v.int8 = htonll(pfld->v.int8);
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+	dst->len[i] = 8;
+}
+
+
+static inline void db_int2pg_int8(struct pg_params* dst, int i,
+								  db_fld_t* src)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+	pfld->v.int4[0] = 0;
+	pfld->v.int4[1] = htonl(src->v.int4);
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+	dst->len[i] = 8;
+}
+
+
+static inline void db_int2pg_bool(struct pg_params* dst, int i, db_fld_t* src)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+	if (src->v.int4) pfld->v.byte[0] = 1;
+	else pfld->v.byte[0] = 0;
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+	dst->len[i] = 1;
+}
+
+
+static inline void db_int2pg_inet(struct pg_params* dst, int i, db_fld_t* src)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+	pfld->v.byte[0] = AF_INET; /* Address family */
+	pfld->v.byte[1] = 32; /* Netmask */
+	pfld->v.byte[2] = 0; /* is CIDR */
+	pfld->v.byte[3] = 4; /* Number of bytes */
+	pfld->v.int4[1] = htonl(src->v.int4); /* Actuall IP address */
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+	dst->len[i] = 8;
+}
+
+
+static inline void db_float2pg_float4(struct pg_params* dst, int i, db_fld_t* src)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+	pfld->v.int4[0] = htonl(src->v.int4);
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+	dst->len[i] = 4;
+}
+
+
+static inline void db_float2pg_float8(struct pg_params* dst, int i, db_fld_t* src)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+	pfld->v.dbl = src->v.flt;
+	pfld->v.int8 = htonll(pfld->v.int8);
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+	dst->len[i] = 8;
+}
+
+
+static inline void db_double2pg_float8(struct pg_params* dst, int i, db_fld_t* src)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+	pfld->v.int8 = htonll(src->v.int8);
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+	dst->len[i] = 8;
+}
+
+
+static inline void db_double2pg_float4(struct pg_params* dst, int i, db_fld_t* src)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+	pfld->v.flt = src->v.dbl;
+	pfld->v.int4[0] = htonl(pfld->v.int4[0]);
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+	dst->len[i] = 4;
+}
+
+
+static inline void db_int2pg_bit(struct pg_params* dst, int i, db_fld_t* src)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+
+	pfld->v.int4[0] = htonl(32);
+	pfld->v.int4[1] = htonl(src->v.int4);
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+   	dst->len[i] = 8;
+}
+
+/*
+static inline void db_int2pg_varbit(struct pg_params* dst, int i,
+						  db_fld_t* src)
+{
+	unsigned int len = 32;
+	struct pg_fld* pfld = DB_GET_PAYLOAD(src);
+
+	pfld->v.int4[0] = htonl(len);
+	pfld->v.int4[1] = htonl(src->v.int4);
+
+	dst->fmt[i] = 1;
+	dst->val[i] = pfld->v.byte;
+   	dst->len[i] = 4 + len / 8 + (len % 8 ? 1 : 0);
+}
+*/
+
+
+static inline void db_str2pg_string(struct pg_params* dst, int i,
+									db_fld_t* src)
+{
+	dst->fmt[i] = 1;
+	dst->val[i] = src->v.lstr.s;
+	dst->len[i] = src->v.lstr.len;
+}
+
+static inline void db_cstr2pg_string(struct pg_params* dst, int i,
+									 db_fld_t* src)
+{
+	dst->fmt[i] = 0;
+	dst->val[i] = src->v.cstr;
+}
+
+
+int pg_fld2pg(struct pg_params* dst, int off, pg_type_t* types,
+			  db_fld_t* src, unsigned int flags)
+{
+	int i;
+	struct pg_fld* pfld;
+
+	if (src == NULL) return 0;
+
+	for(i = 0; !DB_FLD_EMPTY(src) && !DB_FLD_LAST(src[i]); i++) {
+		pfld = DB_GET_PAYLOAD(src + i);
+
+		/* NULL value */
+		if (src[i].flags & DB_NULL) {
+			dst->val[off + i] = NULL;
+			dst->len[off + i] = 0;
+			continue;
+		}
+
+		switch(src[i].type) {
+		case DB_INT:
+			if (pfld->oid == types[PG_INT2].oid)
+				db_int2pg_int2(dst, off + i, src + i);
+			else if (pfld->oid == types[PG_INT4].oid)
+				db_int2pg_int4(dst, off + i, src + i);
+			else if ((pfld->oid == types[PG_TIMESTAMP].oid) ||
+					 (pfld->oid == types[PG_TIMESTAMPTZ].oid))
+				db_int2pg_timestamp(dst, off + i, src + i, flags);
+			else if (pfld->oid == types[PG_INT8].oid)
+				db_int2pg_int8(dst, off + i, src + i);
+			else if (pfld->oid == types[PG_INET].oid)
+				db_int2pg_inet(dst, off + i, src + i);
+			else if (pfld->oid == types[PG_BOOL].oid)
+				db_int2pg_bool(dst, off + i, src + i);
+			else if (pfld->oid == types[PG_BIT].oid)
+				db_int2pg_bit(dst, off + i, src + i);
+			else if (pfld->oid == types[PG_VARBIT].oid)
+				db_int2pg_bit(dst, off + i, src + i);
+			else goto bug;
+			break;
+
+		case DB_BITMAP:
+			if (pfld->oid == types[PG_INT4].oid)
+				db_int2pg_int4(dst, off + i, src + i);
+			else if (pfld->oid == types[PG_INT8].oid)
+				db_int2pg_int8(dst, off + i, src + i);
+			else if (pfld->oid == types[PG_BIT].oid)
+				db_int2pg_bit(dst, off + i, src + i);
+			else if (pfld->oid == types[PG_VARBIT].oid)
+				db_int2pg_bit(dst, off + i, src + i);
+			else goto bug;
+			break;
+
+		case DB_DATETIME:
+			if (pfld->oid == types[PG_INT4].oid)
+				db_int2pg_int4(dst, off + i, src + i);
+			else if ((pfld->oid == types[PG_TIMESTAMP].oid) ||
+					 (pfld->oid == types[PG_TIMESTAMPTZ].oid))
+				db_int2pg_timestamp(dst, off + i, src + i, flags);
+			else if (pfld->oid == types[PG_INT8].oid)
+				db_int2pg_int8(dst, off + i, src + i);
+			else goto bug;
+			break;
+			 
+		case DB_FLOAT:
+			if (pfld->oid == types[PG_FLOAT4].oid)
+				db_float2pg_float4(dst, off + i, src + i);
+			else if (pfld->oid == types[PG_FLOAT8].oid)
+				db_float2pg_float8(dst, off + i, src + i);
+			else goto bug;
+			break;
+
+		case DB_DOUBLE:
+			if (pfld->oid == types[PG_FLOAT4].oid)
+				db_double2pg_float4(dst, off + i, src + i);
+			else if (pfld->oid == types[PG_FLOAT8].oid)
+				db_double2pg_float8(dst, off + i, src + i);
+			else goto bug;
+			break;
+
+		case DB_STR:
+			if (pfld->oid == types[PG_VARCHAR].oid ||
+				pfld->oid == types[PG_BYTE].oid ||
+				pfld->oid == types[PG_CHAR].oid ||
+				pfld->oid == types[PG_TEXT].oid ||
+				pfld->oid == types[PG_BPCHAR].oid)
+				db_str2pg_string(dst, off + i, src + i);
+			else goto bug;
+			break;
+
+		case DB_CSTR:
+			if (pfld->oid == types[PG_VARCHAR].oid ||
+				pfld->oid == types[PG_BYTE].oid ||
+				pfld->oid == types[PG_CHAR].oid ||
+				pfld->oid == types[PG_TEXT].oid ||
+				pfld->oid == types[PG_BPCHAR].oid)
+				db_cstr2pg_string(dst, off + i, src + i);
+			else goto bug;
+			break;
+
+		case DB_BLOB:
+			if (pfld->oid == types[PG_BYTE].oid)
+				db_str2pg_string(dst, off + i, src + i);
+			else goto bug;
+			break;
+
+		default:
+			BUG("postgres: Unsupported field type %d in field %s\n",
+				src[i].type, src[i].name);
+			return -1;
+		}
+	}
+
+	return 0;
+
+ bug:
+	BUG("postgres: Error while converting DB API type %d to Postgres Oid %d\n",
+		src[i].type, pfld->oid);
+	return -1;
+
+}
+
+
+int pg_check_fld2pg(db_fld_t* fld, pg_type_t* types)
+{
+	int i;
+	const char* name = "UNKNOWN";
+	struct pg_fld* pfld;
+
+	if (fld == NULL) return 0;
+
+	for(i = 0; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[i]); i++) {
+		pfld = DB_GET_PAYLOAD(fld + i);
+		switch(fld[i].type) {
+		case DB_INT:
+			if (pfld->oid == types[PG_INT2].oid) continue;
+			if (pfld->oid == types[PG_INT4].oid) continue;
+			if (pfld->oid == types[PG_INT8].oid) continue;
+			if (pfld->oid == types[PG_BOOL].oid) continue;
+			if (pfld->oid == types[PG_INET].oid) continue;
+			if (pfld->oid == types[PG_TIMESTAMP].oid) continue;
+			if (pfld->oid == types[PG_TIMESTAMPTZ].oid) continue;
+			if (pfld->oid == types[PG_BIT].oid) continue;
+			if (pfld->oid == types[PG_VARBIT].oid) continue;
+			break;
+
+		case DB_BITMAP:
+			if (pfld->oid == types[PG_INT4].oid) continue;
+			if (pfld->oid == types[PG_INT8].oid) continue;
+			if (pfld->oid == types[PG_BIT].oid) continue;
+			if (pfld->oid == types[PG_VARBIT].oid) continue;
+			break;
+
+		case DB_FLOAT:
+		case DB_DOUBLE:
+			if (pfld->oid == types[PG_FLOAT4].oid) continue;
+			if (pfld->oid == types[PG_FLOAT8].oid) continue;
+			break;
+
+		case DB_CSTR:
+		case DB_STR:
+			if (pfld->oid == types[PG_BYTE].oid) continue;
+			if (pfld->oid == types[PG_CHAR].oid) continue;
+			if (pfld->oid == types[PG_TEXT].oid) continue;
+			if (pfld->oid == types[PG_BPCHAR].oid) continue;
+			if (pfld->oid == types[PG_VARCHAR].oid) continue;
+			break;
+
+		case DB_BLOB:
+			if (pfld->oid == types[PG_BYTE].oid) continue;
+			break;
+
+		case DB_DATETIME:
+			if (pfld->oid == types[PG_INT4].oid) continue;
+			if (pfld->oid == types[PG_INT8].oid) continue;
+			if (pfld->oid == types[PG_TIMESTAMP].oid) continue;
+			if (pfld->oid == types[PG_TIMESTAMPTZ].oid) continue;
+			break;
+
+		default:
+			BUG("postgres: Unsupported field type %d, bug in postgres module\n",
+				fld[i].type);
+			return -1;
+		}
+
+		pg_oid2name(&name, types, pfld->oid);
+		ERR("postgres: Cannot convert column '%s' of type %s "
+			"to PostgreSQL column type '%s'\n", 
+			fld[i].name, db_fld_str[fld[i].type], name);
+		return -1;
+	}
+	return 0;
+}
+
+
+int pg_resolve_param_oids(db_fld_t* vals, db_fld_t* match, int n1, int n2, PGresult* types)
+{
+	struct pg_fld* pfld;
+	int i;
+
+	if (n1 + n2 != PQnparams(types)) {
+		ERR("postgres: Number of command parameters do not match\n");
+		return -1;
+	}
+
+	for(i = 0; i < n1; i++) {
+		pfld = DB_GET_PAYLOAD(vals + i);
+		pfld->oid = PQparamtype(types, i);
+	}
+
+	for(i = 0; i < n2; i++) {
+		pfld = DB_GET_PAYLOAD(match + i);
+		pfld->oid = PQparamtype(types, n1 + i);
+	}
+
+	return 0;
+}
+
+
+int pg_resolve_result_oids(db_fld_t* fld, int n, PGresult* types)
+{
+	struct pg_fld* pfld;
+	int i;
+	if (fld == NULL) return 0;
+
+	if (n != PQnfields(types)) {
+		ERR("postgres: Result field numbers do not match\n");
+		return -1;
+	}
+
+	for(i = 0; i < n; i++) {
+		pfld = DB_GET_PAYLOAD(fld + i);
+		pfld->oid = PQftype(types, i);
+	}
+
+	return 0;
+}
+
+
+int pg_check_pg2fld(db_fld_t* fld, pg_type_t* types)
+{
+	int i;
+	const char* name = "UNKNOWN";
+	struct pg_fld* pfld;
+
+	if (fld == NULL) return 0;
+
+	for(i = 0; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[i]); i++) {
+		pfld = DB_GET_PAYLOAD(fld + i);
+
+		if (pfld->oid == 0) {
+			ERR("postgres: Unknown type fields not supported\n");
+			return -1;
+		}
+
+		switch(fld[i].type) {
+		case DB_INT:
+			if (pfld->oid == types[PG_INT2].oid) continue;
+			if (pfld->oid == types[PG_INT4].oid) continue;
+			if (pfld->oid == types[PG_INT8].oid) continue;
+			if (pfld->oid == types[PG_BOOL].oid) continue;
+			if (pfld->oid == types[PG_INET].oid) continue;
+			if (pfld->oid == types[PG_TIMESTAMP].oid) continue;
+			if (pfld->oid == types[PG_TIMESTAMPTZ].oid) continue;
+			if (pfld->oid == types[PG_BIT].oid) continue;
+			if (pfld->oid == types[PG_VARBIT].oid) continue;
+			break;
+
+		case DB_BITMAP:
+			if (pfld->oid == types[PG_INT2].oid) continue;
+			if (pfld->oid == types[PG_INT4].oid) continue;
+			if (pfld->oid == types[PG_INT8].oid) continue;
+			if (pfld->oid == types[PG_BIT].oid) continue;
+			if (pfld->oid == types[PG_VARBIT].oid) continue;
+			break;
+
+		case DB_FLOAT:
+			if (pfld->oid == types[PG_FLOAT4].oid) continue;
+			break;
+
+		case DB_DOUBLE:
+			if (pfld->oid == types[PG_FLOAT4].oid) continue;
+			if (pfld->oid == types[PG_FLOAT8].oid) continue;
+			break;
+
+		case DB_CSTR:
+			if (pfld->oid == types[PG_CHAR].oid) continue;
+			if (pfld->oid == types[PG_TEXT].oid) continue;
+			if (pfld->oid == types[PG_BPCHAR].oid) continue;
+			if (pfld->oid == types[PG_VARCHAR].oid) continue;
+			if (pfld->oid == types[PG_INT2].oid) continue;
+			if (pfld->oid == types[PG_INT4].oid) continue;
+			break;
+
+		case DB_STR:
+		case DB_BLOB:
+			if (pfld->oid == types[PG_BYTE].oid) continue;
+			if (pfld->oid == types[PG_CHAR].oid) continue;
+			if (pfld->oid == types[PG_TEXT].oid) continue;
+			if (pfld->oid == types[PG_BPCHAR].oid) continue;
+			if (pfld->oid == types[PG_VARCHAR].oid) continue;
+			if (pfld->oid == types[PG_INT2].oid) continue;
+			if (pfld->oid == types[PG_INT4].oid) continue;
+			break;
+
+		case DB_DATETIME:
+			if (pfld->oid == types[PG_INT2].oid) continue;
+			if (pfld->oid == types[PG_INT4].oid) continue;
+			if (pfld->oid == types[PG_TIMESTAMP].oid) continue;
+			if (pfld->oid == types[PG_TIMESTAMPTZ].oid) continue;
+			break;
+
+		default:
+			BUG("postgres: Unsupported field type %d, bug in postgres module\n",
+				fld[i].type);
+			return -1;
+		}
+
+		pg_oid2name(&name, types, pfld->oid);
+		ERR("postgres: Cannot convert column '%s' of type %s "
+			"to DB API field of type %s\n", 
+			fld[i].name, name, db_fld_str[fld[i].type]);
+		return -1;
+	}
+	return 0;
+}
+
+
+static inline int pg_int2_2_db_cstr(db_fld_t* fld, char* val, int len)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(fld);
+	int size, v;
+
+	v = (int16_t)ntohs(*((int16_t*)val));
+
+    size = snprintf(pfld->buf, INT2STR_MAX_LEN, "%-d", v);
+    if (size < 0 || size >= INT2STR_MAX_LEN) {
+        BUG("postgres: Error while converting integer to string\n");
+        return -1;
+    }
+
+	fld->v.cstr = pfld->buf;
+	return 0;
+}
+
+
+static inline int pg_int4_2_db_cstr(db_fld_t* fld, char* val, int len)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(fld);
+	int size, v;
+
+	v = (int32_t)ntohl(*((int32_t*)val));
+
+    size = snprintf(pfld->buf, INT2STR_MAX_LEN, "%-d", v);
+    if (len < 0 || size >= INT2STR_MAX_LEN) {
+        BUG("postgres: Error while converting integer to string\n");
+        return -1;
+    }
+
+	fld->v.cstr = pfld->buf;
+	return 0;
+}
+
+
+static inline int pg_int2_2_db_str(db_fld_t* fld, char* val, int len)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(fld);
+	int size, v;
+
+	v = (int16_t)ntohs(*((int16_t*)val));
+
+    size = snprintf(pfld->buf, INT2STR_MAX_LEN, "%-d", v);
+    if (size < 0 || size >= INT2STR_MAX_LEN) {
+        BUG("postgres: Error while converting integer to string\n");
+        return -1;
+    }
+
+	fld->v.lstr.s = pfld->buf;
+	fld->v.lstr.len = size;
+	return 0;
+}
+
+
+static inline int pg_int4_2_db_str(db_fld_t* fld, char* val, int len)
+{
+	struct pg_fld* pfld = DB_GET_PAYLOAD(fld);
+	int size, v;
+
+	v = (int32_t)ntohl(*((int32_t*)val));
+
+    size = snprintf(pfld->buf, INT2STR_MAX_LEN, "%-d", v);
+    if (size < 0 || size >= INT2STR_MAX_LEN) {
+        BUG("postgres: Error while converting integer to string\n");
+        return -1;
+    }
+
+	fld->v.lstr.s = pfld->buf;
+	fld->v.lstr.len = size;
+	return 0;
+}
+
+
+static inline int pg_int2_2_db_int(db_fld_t* fld, char* val, int len)
+{
+	fld->v.int4 = (int16_t)ntohs(*((int16_t*)val));
+	return 0;
+}
+
+
+static inline int pg_int4_2_db_int(db_fld_t* fld, char* val, int len)
+{
+	fld->v.int4 = (int32_t)ntohl(*((int32_t*)val));
+	return 0;
+}
+
+
+static inline int pg_int8_2_db_int(db_fld_t* fld, char* val, int len)
+{
+	fld->v.int8 = (int64_t)ntohll(*((int64_t*)val));
+	return 0;
+}
+
+
+static inline int pg_bool2db_int(db_fld_t* fld, char* val, int len)
+{
+	fld->v.int4 = val[0];
+	return 0;
+}
+
+
+static inline int pg_inet2db_int(db_fld_t* fld, char* val, int len)
+{
+	if (len != 8 || val[2] != 0) {
+		ERR("postgres: Unsupported 'inet' format, column %s\n", fld->name);
+		return -1;
+	}
+
+	if (val[0] != AF_INET) {
+		ERR("postgres: Unsupported address family %d in field %s\n",
+			val[0], fld->name);
+		return -1;
+	}
+
+	if (val[1] != 32) {
+		WARN("postgres: Netmasks shorter than 32-bits not supported, "
+			 "column %s\n", fld->name);
+	}
+
+	if (val[3] != 4) {
+		ERR("postgres: Unsupported IP address size %d in column %s\n",
+			val[3], fld->name);
+		return -1;
+	}
+
+	fld->v.int4 = (int32_t)ntohl(((int32_t*)val)[1]);
+	return 0;
+}
+
+
+static inline int pg_timestamp2db_int(db_fld_t* fld, char* val, int len, 
+									  unsigned int flags)
+{
+	if (flags & PG_INT8_TIMESTAMP) {
+		/* int8 format */
+		fld->v.int4 = (int64_t)ntohll(((int64_t*)val)[0]) / (int64_t)1000000 + PG_EPOCH_TIME;
+	} else {
+		/* double format */
+		fld->v.int4 = PG_EPOCH_TIME + ntohll(((int64_t*)val)[0]);
+	}
+	return 0;
+}
+
+
+static inline int pg_bit2db_int(db_fld_t* fld, char* val, int len)
+{
+	int size;
+
+	size = ntohl(*(uint32_t*)val);
+	if (size != 32) {
+		ERR("postgres: Unsupported bit field size (%d), column %s\n",
+			size, fld->name);
+		return -1;
+	}
+	fld->v.int4 = ntohl(((uint32_t*)val)[1]);
+	return 0;
+}
+
+
+static inline int pg_float42db_float(db_fld_t* fld, char* val, int len)
+{
+	fld->v.int4 = (uint32_t)ntohl(*(uint32_t*)val);
+	return 0;
+}
+
+
+static inline int pg_float42db_double(db_fld_t* fld, char* val, int len)
+{
+	float tmp;
+
+	tmp = ntohl(*(uint32_t*)val);
+	fld->v.dbl = tmp;
+	return 0;
+}
+
+
+static inline int pg_float82db_double(db_fld_t* fld, char* val, int len)
+{
+	fld->v.int8 = ntohll(*(uint64_t*)val);
+	return 0;
+}
+
+
+static inline int pg_string2db_cstr(db_fld_t* fld, char* val, int len)
+{
+	fld->v.cstr = val;
+	return 0;
+}
+
+
+static inline int pg_string2db_str(db_fld_t* fld, char* val, int len)
+{
+	fld->v.lstr.s = val;
+	fld->v.lstr.len = len;
+	return 0;
+}
+
+
+
+int pg_pg2fld(db_fld_t* dst, PGresult* src, int row, 
+			  pg_type_t* types, unsigned int flags)
+{
+	char* val;
+	int i, len, ret;
+	Oid type;
+	
+	if (dst == NULL || src == NULL) return 0;
+	ret = 0;
+
+	for(i = 0; !DB_FLD_EMPTY(dst) && !DB_FLD_LAST(dst[i]); i++) {
+		if (PQgetisnull(src, row, i)) {
+			dst[i].flags |= DB_NULL;
+			continue;
+		} else {
+			dst[i].flags &= ~DB_NULL;
+		}
+
+		type = PQftype(src, i);
+		val = PQgetvalue(src, row, i);
+		len = PQgetlength(src, row, i);		
+
+		switch(dst[i].type) {
+		case DB_INT:
+			if (type == types[PG_INT2].oid)
+				ret |= pg_int2_2_db_int(dst + i, val, len);
+			else if (type == types[PG_INT4].oid)
+				ret |= pg_int4_2_db_int(dst + i, val, len);
+			else if (type == types[PG_INT8].oid)
+				ret |= pg_int8_2_db_int(dst + i, val, len);
+			else if (type == types[PG_BOOL].oid)
+				ret |= pg_bool2db_int(dst + i, val, len);
+			else if (type == types[PG_INET].oid)
+				ret |= pg_inet2db_int(dst + i, val, len);
+			else if ((type == types[PG_TIMESTAMP].oid) ||
+					 (type == types[PG_TIMESTAMPTZ].oid))
+				ret |= pg_timestamp2db_int(dst + i, val, len, flags);
+			else if (type == types[PG_BIT].oid)
+				ret |= pg_bit2db_int(dst + i, val, len);
+			else if (type == types[PG_VARBIT].oid)
+				ret |= pg_bit2db_int(dst + i, val, len);
+			else goto bug;
+			break;
+
+		case DB_FLOAT:
+			if (type == types[PG_FLOAT4].oid)
+				ret |= pg_float42db_float(dst + i, val, len);
+			else goto bug;
+			break;
+
+		case DB_DOUBLE:
+			if (type == types[PG_FLOAT4].oid)
+				ret |= pg_float42db_double(dst + i, val, len);
+			else if (type == types[PG_FLOAT8].oid)
+				ret |= pg_float82db_double(dst + i, val, len);
+			else goto bug;
+			break;
+
+		case DB_DATETIME:
+			if (type == types[PG_INT2].oid)
+				ret |= pg_int2_2_db_int(dst + i, val, len);
+			else if (type == types[PG_INT4].oid)
+				ret |= pg_int4_2_db_int(dst + i, val, len);
+			else if ((type == types[PG_TIMESTAMP].oid) ||
+					 (type == types[PG_TIMESTAMPTZ].oid))
+				ret |= pg_timestamp2db_int(dst + i, val, len, flags);
+			else goto bug;
+			break;
+
+		case DB_CSTR:
+			if ((type == types[PG_CHAR].oid) ||
+				(type == types[PG_TEXT].oid) ||
+				(type == types[PG_BPCHAR].oid) ||
+				(type == types[PG_VARCHAR].oid))
+				ret |= pg_string2db_cstr(dst + i, val, len);
+			else if (type == types[PG_INT2].oid)
+				ret |= pg_int2_2_db_cstr(dst + i, val, len);
+			else if (type == types[PG_INT4].oid)
+				ret |= pg_int4_2_db_cstr(dst + i, val, len);
+			else goto bug;
+			break;
+
+		case DB_STR:
+		case DB_BLOB:
+			if ((type == types[PG_BYTE].oid) ||
+				(type == types[PG_CHAR].oid) ||
+				(type == types[PG_TEXT].oid) ||
+				(type == types[PG_BPCHAR].oid) ||
+				(type == types[PG_VARCHAR].oid))
+				ret |= pg_string2db_str(dst + i, val, len);
+			else if (type == types[PG_INT2].oid)
+				ret |= pg_int2_2_db_str(dst + i, val, len);
+			else if (type == types[PG_INT4].oid)
+				ret |= pg_int4_2_db_str(dst + i, val, len);
+			else goto bug;
+			break;
+
+		case DB_BITMAP:
+			if (type == types[PG_INT2].oid)
+				ret |= pg_int2_2_db_int(dst + i, val, len);
+			else if (type == types[PG_INT4].oid)
+				ret |= pg_int4_2_db_int(dst + i, val, len);
+			else if (type == types[PG_INT8].oid)
+				ret |= pg_int8_2_db_int(dst + i, val, len);
+			else if (type == types[PG_BIT].oid)
+				ret |= pg_bit2db_int(dst + i, val, len);
+			else if (type == types[PG_VARBIT].oid)
+				ret |= pg_bit2db_int(dst + i, val, len);
+			else goto bug;
+			break;
+
+		default:
+			BUG("postgres: Unsupported field type %d in field %s\n",
+				dst[i].type, dst[i].name);
+			return -1;			
+		}
+	}
+	return ret;
+
+ bug:
+	BUG("postgres: Error while converting Postgres Oid %d to DB API type %d\n",
+		type, dst[i].type);
+	return -1;
+}
+
+
+/** @} */

+ 148 - 0
modules/db_postgres/pg_fld.h

@@ -0,0 +1,148 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+#ifndef _PG_FLD_H
+#define _PG_FLD_H
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file 
+ * Implementation of pg_fld data structure representing PostgreSQL fields and
+ * related functions.
+ */
+
+#include "pg_oid.h"
+#include "pg_cmd.h"
+#include "../../ut.h"
+#include "../../lib/srdb2/db_gen.h"
+#include "../../lib/srdb2/db_fld.h"
+#include <libpq-fe.h>
+
+struct pg_fld {
+	db_drv_t gen;
+
+	/**
+	 * A union of varius data types from db_fld, postgres expects binary
+	 * data in network byte order so we use these variables as temporary
+	 * buffer to store values after the conversion.
+	 */
+	union {
+		int          int4[2]; /**< Integer value in network byte order */
+		short        int2[4];
+		float        flt;     /**< Float value in network byte order */
+		double       dbl;     /**< Double value in network byte order */
+		time_t       time;    /**< Unix timestamp in network byte order */
+		unsigned int bitmap;  /**< Bitmap value in network byte order */ 
+		long long    int8;    /**< 8-byte integer value in network byte order */
+		char         byte[8];
+	} v;
+	char buf[INT2STR_MAX_LEN]; /**< Buffer for int2str conversions */
+	Oid oid;                   /**< Type of the field on the server */
+};
+
+
+/** Creates a new PostgreSQL specific payload.
+ * This function creates a new PostgreSQL specific payload structure and
+ * attaches the structure to the generic db_fld structure.
+ * @param fld A generic db_fld structure to be exended.
+ * @param table Name of the table on the server.
+ * @retval 0 on success.
+ * @retval A negative number on error.
+ */
+int pg_fld(db_fld_t* fld, char* table);
+
+int pg_resolve_param_oids(db_fld_t* vals, db_fld_t* match, 
+						  int n1, int n2, PGresult* res);
+
+int pg_resolve_result_oids(db_fld_t* fld, int n, PGresult* res);
+
+
+/** Converts arrays of db_fld fields to PostgreSQL parameters.
+ * The function converts fields in SER db_fld format to parameters suitable
+ * for PostgreSQL API functions.
+ * @param values An array of pointers to values in PostgreSQL format. The
+ *               function will store pointers to converted values there.
+ * @param lenghts An array of integers that will be filled with lenghts
+ *                of values.
+ * @param formats An array of value formats, see PostgreSQL API client
+ *                library documentation for more detail.
+ * @param oids Types of corresponding columns on the server.
+ * @param types A type conversion table.
+ * @param fld An array of db_fld fields to be converted.
+ * @param flags Connection flags controlling how values are converted.
+ * @todo Implement support for bit fields with size bigger than 32 
+ * @todo Implement support for varbit properly to remove leading zeroes
+ * @todo Check if timezones are handled properly
+ * @todo Support for DB_NONE in pg_pg2fld and pg_check_pg2fld
+ * @todo local->UTC conversion (also check the SQL command in ser-oob)
+ */
+int pg_fld2pg(struct pg_params* dst, int off, pg_type_t* types, 
+			  db_fld_t* src, unsigned int flags);
+
+
+/** Converts fields from result in PGresult format into SER format.
+ * The function converts fields from PostgreSQL result (PGresult structure)
+ * into the internal format used in SER. The function converts one row at a
+ * time.
+ * @param fld The destination array of db_fld fields to be filled with converted
+ *            values.
+ * @param pres A PostgreSQL result structure to be converted into SER format.
+ * @param row Number of the row to be converted.
+ * @param flags Connection flags controlling how values are converted.
+ * @retval 0 on success
+ * @retval A negative number on error.
+ * @todo UTC->local conversion
+ */
+int pg_pg2fld(db_fld_t* dst, PGresult* src, int row, pg_type_t* types, 
+			  unsigned int flags);
+
+
+/** Checks if all db_fld fields have types compatible with corresponding field 
+ * types on the server.
+ * The functions checks whether all db_fld fields in the last parameter are
+ * compatible with column types on the server.
+ * @param oids An array of Oids of corresponding columns on the server.
+ * @param lenghts An array of sizes of corresponding columns on the server.
+ * @param types An array used to map internal field types to Oids.
+ * @param fld An array of db_fld fields to be checked.
+ * @retval 0 on success
+ * @retval A negative number on error.
+ */
+int pg_check_fld2pg(db_fld_t* fld, pg_type_t* types);
+
+
+int pg_check_pg2fld(db_fld_t* fld, pg_type_t* types);
+
+
+/** @} */
+
+#endif /* _PG_FLD_H */

+ 546 - 0
modules/db_postgres/pg_mod.c

@@ -0,0 +1,546 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file 
+ * Postgres module interface.
+ */
+
+#include "pg_mod.h"
+#include "pg_uri.h"
+#include "pg_con.h"
+#include "pg_cmd.h"
+#include "pg_res.h"
+#include "pg_fld.h"
+#include "km_db_postgres.h"
+
+#include "../../sr_module.h"
+
+#ifdef PG_TEST
+#include <limits.h>
+#include <float.h>
+#endif
+
+static int pg_mod_init(void);
+
+MODULE_VERSION
+
+int pg_connect_timeout = 0;  /* Default is unlimited */
+int pg_retries = 2;  /* How many times should the module try re-execute failed commands.
+					  * 0 disables reconnecting */
+
+/*
+ * Postgres module interface
+ */
+static cmd_export_t cmds[] = {
+	{"db_ctx",    (cmd_function)NULL, 0, 0, 0},
+	{"db_con",    (cmd_function)pg_con, 0, 0, 0},
+	{"db_uri",    (cmd_function)pg_uri, 0, 0, 0},
+	{"db_cmd",    (cmd_function)pg_cmd, 0, 0, 0},
+	{"db_put",    (cmd_function)pg_cmd_exec, 0, 0, 0},
+	{"db_del",    (cmd_function)pg_cmd_exec, 0, 0, 0},
+	{"db_get",    (cmd_function)pg_cmd_exec, 0, 0, 0},
+	{"db_upd",    (cmd_function)pg_cmd_exec, 0, 0, 0},
+	{"db_sql",    (cmd_function)pg_cmd_exec, 0, 0, 0},
+	{"db_res",    (cmd_function)pg_res, 0, 0, 0},
+	{"db_fld",    (cmd_function)pg_fld, 0, 0, 0},
+	{"db_first",  (cmd_function)pg_cmd_first, 0, 0, 0},
+	{"db_next",   (cmd_function)pg_cmd_next, 0, 0, 0},
+	{"db_setopt", (cmd_function)pg_setopt, 0, 0, 0},
+	{"db_getopt", (cmd_function)pg_getopt, 0, 0, 0},
+	{"db_bind_api", (cmd_function)db_postgres_bind_api, 0, 0, 0},
+	{0, 0, 0, 0, 0}
+};
+
+
+/*
+ * Exported parameters
+ */
+static param_export_t params[] = {
+	{"retries",         PARAM_INT, &pg_retries },
+	{0, 0, 0}
+};
+
+
+struct module_exports exports = {
+	"db_postgres",
+	cmds,
+	0,            /* RPC method */
+	params,       /* module parameters */
+	pg_mod_init,  /* module initialization function */
+	0,            /* response function*/
+	0,            /* destroy function */
+	0,            /* oncancel function */
+	0             /* per-child init function */
+};
+
+/*
+CREATE TABLE test (
+    col_bool BOOL,
+    col_bytea BYTEA,
+    col_char CHAR,
+    col_int8 INT8,
+    col_int4 INT4,
+    col_int2 INT2,
+    col_text TEXT,
+    col_float4 FLOAT4,
+    col_float8 FLOAT8,
+    col_inet INET,
+    col_bpchar BPCHAR,
+    col_varchar VARCHAR,
+    col_timestamp TIMESTAMP,
+    col_timestamptz TIMESTAMPTZ,
+    col_bit BIT(32),
+    col_varbit VARBIT
+);
+*/
+
+
+#ifdef PG_TEST
+int pg_test(void)
+{
+	int i, row;
+	db_ctx_t* db;
+	db_cmd_t* put, *del, *get;
+	db_res_t* result;
+	db_rec_t* rec;
+	char* times;
+
+	db_fld_t int_vals[] = {
+		{.name = "col_bool",        .type = DB_INT},
+		{.name = "col_int8",        .type = DB_INT},
+		{.name = "col_int4",        .type = DB_INT},
+		{.name = "col_inet",        .type = DB_INT},
+		{.name = "col_timestamp",   .type = DB_INT},
+		{.name = "col_timestamptz", .type = DB_INT},
+		{.name = "col_bit",         .type = DB_INT},
+		{.name = "col_varbit",      .type = DB_INT},
+		{.name = NULL}
+	};
+
+	db_fld_t datetime_vals[] = {
+		{.name = "col_int8",        .type = DB_INT},
+		{.name = "col_int4",        .type = DB_INT},
+		{.name = "col_timestamp",   .type = DB_INT},
+		{.name = "col_timestamptz", .type = DB_INT},
+		{.name = NULL}
+	};
+
+
+	db_fld_t bitmap_vals[] = {
+		{.name = "col_int8",      .type = DB_INT},
+		{.name = "col_int4",      .type = DB_INT},
+		{.name = "col_bit",       .type = DB_INT},
+		{.name = "col_varbit",    .type = DB_INT},
+		{.name = NULL}
+	};
+
+	db_fld_t float_vals[] = {
+		{.name = "col_float4", .type = DB_FLOAT},
+		{.name = "col_float8", .type = DB_FLOAT},
+		{.name = NULL}
+	};
+
+	db_fld_t double_vals[] = {
+		{.name = "col_float8", .type = DB_DOUBLE},
+		{.name = NULL}
+	};
+
+	db_fld_t str_vals[] = {
+		{.name = "col_varchar", .type = DB_STR},
+		{.name = "col_bytea",   .type = DB_STR},
+		{.name = "col_text",    .type = DB_STR},
+		{.name = "col_bpchar",  .type = DB_STR},
+		{.name = "col_char",    .type = DB_STR},
+		{.name = NULL}
+	};
+
+	db_fld_t cstr_vals[] = {
+		{.name = "col_varchar", .type = DB_CSTR},
+		{.name = "col_bytea",   .type = DB_CSTR},
+		{.name = "col_text",    .type = DB_CSTR},
+		{.name = "col_bpchar",  .type = DB_CSTR},
+		{.name = "col_char",    .type = DB_CSTR},
+		{.name = NULL}
+	};
+
+	db_fld_t blob_vals[] = {
+		{.name = "col_bytea",   .type = DB_BLOB},
+		{.name = NULL}
+	};
+
+
+	db_fld_t res[] = {
+		{.name = "col_bool",        .type = DB_INT},
+		{.name = "col_bytea",       .type = DB_BLOB},
+		{.name = "col_char",        .type = DB_STR},
+		{.name = "col_int8",        .type = DB_INT},
+		{.name = "col_int4",        .type = DB_INT},
+		{.name = "col_int2",        .type = DB_INT},
+		{.name = "col_text",        .type = DB_STR},
+		{.name = "col_float4",      .type = DB_FLOAT},
+		{.name = "col_float8",      .type = DB_DOUBLE},
+		{.name = "col_inet",        .type = DB_INT},
+		{.name = "col_bpchar",      .type = DB_STR},
+		{.name = "col_varchar",     .type = DB_STR},
+		{.name = "col_timestamp",   .type = DB_DATETIME},
+		{.name = "col_timestamptz", .type = DB_DATETIME},
+		{.name = "col_bit",         .type = DB_BITMAP},
+		{.name = "col_varbit",      .type = DB_BITMAP},
+		{.name = NULL}
+	};
+
+
+	db = db_ctx("postgres");
+	if (db == NULL) {
+		ERR("Error while initializing database layer\n");
+		goto error;
+	}
+	if (db_add_db(db, "postgres://janakj:heslo@localhost/ser") < 0) goto error;
+	if (db_connect(db) < 0) goto error;
+	
+	del = db_cmd(DB_DEL, db, "test", NULL, NULL, NULL);
+	if (del == NULL) {
+		ERR("Error while building delete * query\n");
+		goto error;
+	}
+
+    put = db_cmd(DB_PUT, db, "test", NULL, NULL, int_vals);
+	if (put == NULL) {
+		ERR("Error while building test query\n");
+		goto error;
+	}
+
+	if (db_exec(NULL, del)) {
+		ERR("Error while deleting rows from test table\n");
+		goto error;
+	}
+
+	put->vals[0].v.int4 = 0xffffffff;
+	put->vals[1].v.int4 = 0xffffffff;
+	put->vals[2].v.int4 = 0xffffffff;
+	put->vals[3].v.int4 = 0xffffffff;
+	put->vals[4].v.int4 = 0xffffffff;
+	put->vals[5].v.int4 = 0xffffffff;
+	put->vals[6].v.int4 = 0xffffffff;
+	put->vals[7].v.int4 = 0xffffffff;
+
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	put->vals[0].v.int4 = 0;
+	put->vals[1].v.int4 = 0;
+	put->vals[2].v.int4 = 0;
+	put->vals[3].v.int4 = 0;
+	put->vals[4].v.int4 = 0;
+	put->vals[5].v.int4 = 0;
+	put->vals[6].v.int4 = 0;
+	put->vals[7].v.int4 = 0;
+
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	db_cmd_free(put);
+
+	put = db_cmd(DB_PUT, db, "test", NULL, NULL, bitmap_vals);
+	if (put == NULL) {
+		ERR("Error while building bitmap test query\n");
+		goto error;
+	}
+
+	put->vals[0].v.int4 = 0xffffffff;
+	put->vals[1].v.int4 = 0xffffffff;
+	put->vals[2].v.int4 = 0xffffffff;
+	put->vals[3].v.int4 = 0xffffffff;
+	put->vals[4].v.int4 = 0xffffffff;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	put->vals[0].v.int4 = 0;
+	put->vals[1].v.int4 = 0;
+	put->vals[2].v.int4 = 0;
+	put->vals[3].v.int4 = 0;
+	put->vals[4].v.int4 = 0;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	db_cmd_free(put);
+
+	put = db_cmd(DB_PUT, db, "test", NULL, NULL, float_vals);
+	if (put == NULL) {
+		ERR("Error while building float test query\n");
+		goto error;
+	}
+
+	put->vals[0].v.flt = FLT_MAX;
+	put->vals[1].v.flt = FLT_MAX;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	put->vals[0].v.flt = FLT_MIN;
+	put->vals[1].v.flt = FLT_MIN;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	db_cmd_free(put);
+
+	put = db_cmd(DB_PUT, db, "test", NULL, NULL, double_vals);
+	if (put == NULL) {
+		ERR("Error while building double test query\n");
+		goto error;
+	}
+
+	put->vals[0].v.dbl = DBL_MAX;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	put->vals[0].v.dbl = DBL_MIN;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+
+	db_cmd_free(put);
+
+	put = db_cmd(DB_PUT, db, "test", NULL, NULL, str_vals);
+	if (put == NULL) {
+		ERR("Error while building str test query\n");
+		goto error;
+	}
+
+	put->vals[0].v.lstr.s = "";
+	put->vals[0].v.lstr.len = 0;
+	put->vals[1].v.lstr.s = "";
+	put->vals[1].v.lstr.len = 0;
+	put->vals[2].v.lstr.s = "";
+	put->vals[2].v.lstr.len = 0;
+	put->vals[3].v.lstr.s = "";
+	put->vals[3].v.lstr.len = 0;
+	put->vals[4].v.lstr.s = "";
+	put->vals[4].v.lstr.len = 0;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	put->vals[0].v.lstr.s = "abc should not be there";
+	put->vals[0].v.lstr.len = 3;
+	put->vals[1].v.lstr.s = "abc should not be there";
+	put->vals[1].v.lstr.len = 3;
+	put->vals[2].v.lstr.s = "abc should not be there";
+	put->vals[2].v.lstr.len = 3;
+	put->vals[3].v.lstr.s = "abc should not be there";
+	put->vals[3].v.lstr.len = 3;
+	put->vals[4].v.lstr.s = "a should not be there";
+	put->vals[4].v.lstr.len = 1;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	db_cmd_free(put);
+
+	put = db_cmd(DB_PUT, db, "test", NULL, NULL, cstr_vals);
+	if (put == NULL) {
+		ERR("Error while building cstr test query\n");
+		goto error;
+	}
+
+	put->vals[0].v.cstr = "";
+	put->vals[1].v.cstr = "";
+	put->vals[2].v.cstr = "";
+	put->vals[3].v.cstr = "";
+	put->vals[4].v.cstr = "";
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	put->vals[0].v.cstr = "def";
+	put->vals[1].v.cstr = "def";
+	put->vals[2].v.cstr = "def";
+	put->vals[3].v.cstr = "def";
+	put->vals[4].v.cstr = "d";
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	db_cmd_free(put);
+
+	put = db_cmd(DB_PUT, db, "test", NULL, NULL, blob_vals);
+	if (put == NULL) {
+		ERR("Error while building blob test query\n");
+		goto error;
+	}
+
+	put->vals[0].v.blob.s = "\0\0\0\0";
+	put->vals[0].v.blob.len = 4;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+
+	db_cmd_free(put);
+
+	put = db_cmd(DB_PUT, db, "test", NULL, NULL, datetime_vals);
+	if (put == NULL) {
+		ERR("Error while building datetime test query\n");
+		goto error;
+	}
+
+	put->vals[0].v.time = 0xffffffff;
+	put->vals[1].v.time = 0xffffffff;
+	put->vals[2].v.time = 0xffffffff;
+	put->vals[3].v.time = 0xffffffff;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	put->vals[0].v.time = 0;
+	put->vals[1].v.time = 0;
+	put->vals[2].v.time = 0;
+	put->vals[3].v.time = 0;
+	if (db_exec(NULL, put)) {
+		ERR("Error while executing database command\n");
+		goto error;
+	}
+
+	if (put) db_cmd_free(put);
+	if (del) db_cmd_free(del);
+	put = NULL;
+	del = NULL;
+
+
+	get = db_cmd(DB_GET, db, "test", res, NULL, NULL);
+	if (get == NULL) {
+		ERR("Error while building select query\n");
+		goto error;
+	}
+
+	if (db_exec(&result, get)) {
+		ERR("Error while executing select query\n");
+		goto error;
+	}
+
+	rec = db_first(result);
+	row = 1;
+	while(rec) {
+		ERR("row: %d\n", row);
+		for(i = 0; !DB_FLD_LAST(rec->fld[i]); i++) {
+			if (rec->fld[i].flags & DB_NULL) {
+				ERR("%s: NULL\n", rec->fld[i].name);
+			} else {
+				switch(rec->fld[i].type) {
+				case DB_INT:
+				case DB_BITMAP:
+					ERR("%s: %d\n", rec->fld[i].name, rec->fld[i].v.int4);
+					break;
+					
+				case DB_DATETIME:
+					times = ctime(&rec->fld[i].v.time);
+					ERR("%s: %d:%.*s\n", rec->fld[i].name, rec->fld[i].v.time, strlen(times) - 1, times);
+					break;
+					
+				case DB_DOUBLE:
+					ERR("%s: %f\n", rec->fld[i].name, rec->fld[i].v.dbl);
+					break;
+					
+				case DB_FLOAT:
+					ERR("%s: %f\n", rec->fld[i].name, rec->fld[i].v.flt);
+					break;
+					
+				case DB_STR:
+				case DB_BLOB:
+					ERR("%s: %.*s\n", rec->fld[i].name, rec->fld[i].v.lstr.len, rec->fld[i].v.lstr.s);
+					break;
+					
+				case DB_CSTR:
+					ERR("%s: %s\n", rec->fld[i].name, rec->fld[i].v.cstr);
+					break;
+				}
+			}
+		}
+		ERR("\n");
+		rec = db_next(result);
+		row++;
+	}
+
+	db_res_free(result);
+
+	db_cmd_free(get);
+	db_disconnect(db);
+	db_ctx_free(db);
+	return 0;
+
+ error:
+	if (get) db_cmd_free(get);
+	if (put) db_cmd_free(put);
+	if (del) db_cmd_free(del);
+	db_disconnect(db);
+	db_ctx_free(db);
+	return -1;
+}
+#endif /* PG_TEST */
+
+static int pg_mod_init(void)
+{
+#ifdef PG_TEST
+	if (pg_test() == 0) {
+		ERR("postgres: Testing successful\n");
+	} else {
+		ERR("postgres: Testing failed\n");
+	}
+	return -1;
+#endif /* PG_TEST */
+	return km_postgres_mod_init();
+}
+
+/** @} */

+ 47 - 0
modules/db_postgres/pg_mod.h

@@ -0,0 +1,47 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+#ifndef _PG_MOD_H
+#define _PG_MOD_H
+
+/** @defgroup postgres PostgreSQL Database Driver
+ * @ingroup DB_API 
+ */
+/** @{ */
+
+/** \file 
+ * Postgres module interface.
+ */
+
+extern int pg_retries;
+
+/** @} */
+
+#endif /* _PG_MOD_H */

+ 184 - 0
modules/db_postgres/pg_oid.c

@@ -0,0 +1,184 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file
+ * Implementation of functions related to PostgreSQL Oid identifiers.
+ */
+
+
+#include "pg_oid.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include <strings.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** An array of supported PostgreSQL field types. */
+static char* pg_type_id_name[] = {
+	"bool",
+	"bytea",
+	"char",
+	"int8",
+	"int2",
+	"int4",
+	"text",
+	"float4",
+	"float8",
+	"inet",
+	"bpchar",
+	"varchar",
+	"timestamp",
+	"timestamptz",
+	"bit",
+	"varbit",
+};
+
+
+static int get_index(char* name)
+{
+	int i;
+	
+	for(i = 0; i < PG_ID_MAX; i++) {
+		if (strcasecmp(name, pg_type_id_name[i]) == 0) return i; 
+	}
+	return -1;
+}
+
+
+pg_type_t* pg_new_oid_table(PGresult* res)
+ {
+	pg_type_t* table = NULL;
+	int row, n = 0, end, idx, fields;
+	str s;
+	
+	if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) goto error;
+
+	n = PQntuples(res);
+	if (n <= 0) goto error;
+	fields = PQnfields(res);
+	if (fields != 2) goto error;
+
+	table = (pg_type_t*)malloc(sizeof(pg_type_t) * (n + 1));
+	if (table == NULL) goto error;
+	memset(table, '\0', sizeof(pg_type_t) * (n + 1));
+	
+	end = n - 1;
+	for(row = 0; row < n; row++) {
+
+		/* Get name */
+		s.s = PQgetvalue(res, row, 0);
+		if (s.s == NULL) goto error;
+
+		/* Find index where the record is to be stored */
+		idx = get_index(s.s);
+		if (idx == -1) idx = end--;
+
+		/* Store the name */
+		table[idx].name = strdup(s.s);
+		if (table[idx].name == NULL) goto error;
+
+		/* Oid */
+		s.s = PQgetvalue(res, row, 1);
+		if (s.s == NULL) goto error;
+		s.len = strlen(s.s);
+		if (str2int(&s, &table[idx].oid) < 0) goto error;
+
+		DBG("postgres: Type %s maps to Oid %d\n", table[idx].name, table[idx].oid);
+	}
+	return table;
+	
+ error:
+	ERR("postgres: Error while obtaining field/data type description from server\n");
+	if (table) {
+		for(idx = 0; idx < n; idx++) {
+			if (table[idx].name) free(table[idx].name);
+		}
+		free(table);
+	}
+	return NULL;
+}
+
+
+void pg_destroy_oid_table(pg_type_t* table)
+{
+	int i;
+	if (table) {
+		for(i = 0; table[i].name; i++) {
+			free(table[i].name);
+		}
+		free(table);
+	}
+}
+
+
+int pg_name2oid(Oid* oid, pg_type_t* table, const char* name)
+{
+	int i;
+
+	if (!oid || !table) {
+		BUG("postgres: Invalid parameters to pg_name2oid\n");
+		return -1;
+	}
+
+	if (name == NULL || name[0] == '\0') return 1;
+
+	for(i = 0; table[i].name; i++) {
+		if (strcasecmp(table[i].name, name) == 0) {
+			*oid = table[i].oid;
+			return 0;
+		}
+	}
+	return 1;
+}
+
+
+int pg_oid2name(const char** name, pg_type_t* table, Oid oid)
+{
+	int i;
+
+	if (!table || !name) {
+		BUG("postgres: Invalid parameters to pg_oid2name\n");
+		return -1;
+	}
+
+	for(i = 0; table[i].name; i++) {
+		if (oid == table[i].oid) {
+			*name = table[i].name;
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/** @} */

+ 128 - 0
modules/db_postgres/pg_oid.h

@@ -0,0 +1,128 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+#ifndef _PG_OID_H
+#define _PG_OID_H
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file
+ * Data structures and functions implementing support for PostgreSQL Oid
+ * identifiers.
+ */
+
+#include <libpq-fe.h>
+
+/** Structure mapping field names to Oids.
+ * This structure is used to map field names or data type names to their
+ * Oids/field types.
+ */
+typedef struct pg_type {
+	Oid oid;    /**< PostgreSQL Oid (object identifier) */
+	char* name; /**< Field name */
+} pg_type_t;
+
+
+/** Enumeration of supported PostreSQL types.
+ * This is the enumeration of all PostgreSQL types supported
+ * by this driver, that means this driver will be able to convert
+ * to and from these types.
+ *
+ * This enum is primarilly used as index to arrays of pg_type_t that are
+ * stored in pg_con structures. Upon connecting to a PostgreSQL server the
+ * driver retrieves the list of supported data types and oids from the server
+ * and stores then in an array. Different PostgreSQL servers can have
+ * different Oids for various data types so we have to have one array that
+ * maps symbolic names below to Oids per connection/server.
+ */
+enum pg_type_id {
+	PG_BOOL = 0,    /**< Boolean, true/false */
+	PG_BYTE,        /**< Binary data */
+	PG_CHAR,        /**< Single character */
+	PG_INT8,        /**< Integer with 8-byte storage */
+	PG_INT2,        /**< Integer with 2-byte storage */
+	PG_INT4,        /**< Integer with 4-byte storage */
+	PG_TEXT,        /**< Variable-length string, no limit specified */
+	PG_FLOAT4,      /**< Single-precision floating point number, 4-byte storage */
+	PG_FLOAT8,      /**< Double-precision floating point number, 8-byte storage */
+	PG_INET,        /**< IP address/netmask, host address */
+	PG_BPCHAR,      /**< Blank-padded string, fixed storage length */
+	PG_VARCHAR,     /**< Non-blank padded string, variable storage length */
+	PG_TIMESTAMP,   /**< Date and time */
+	PG_TIMESTAMPTZ, /**< Date and time with time zone */
+	PG_BIT,         /**< Fixed-length bit string */
+	PG_VARBIT,      /**< Variable-length bit string */
+	PG_ID_MAX       /**< Bumper, this must be the last element of the enum */
+};
+
+
+/** Creates a new Oid mapping table.
+ * The function creates a new Oid mapping table and initalizes the contents
+ * of the table with values obtained from the PostgreSQL result in parameter.
+ * Each element of the table maps field type name to oid and vice versa.
+ * @param res A PostgreSQL result structure used to initialize the array.
+ * @retval A pointer to the resulting array.
+ * @retval NULL on error.
+ */
+pg_type_t* pg_new_oid_table(PGresult* res);
+
+
+/** Frees all memory used by the table
+ * @param table A pointer to table to be freed
+ */
+void pg_destroy_oid_table(pg_type_t* table);
+
+
+/** Maps a field type name to Oid.
+ * @param oid The resulting oid
+ * @param table The mapping table
+ * @param name Field type name
+ * @retval 0 on success
+ * @retval 1 if the type name is unknown
+ * @retval -1 on error.
+ */
+int pg_name2oid(Oid* oid, pg_type_t* table, const char* name);
+
+
+/** Maps a field type name to Oid.
+ * @param oid The resulting oid
+ * @param table The mapping table
+ * @param name Field type name
+ * @retval 0 on success
+ * @retval 1 if the type name is unknown
+ * @retval -1 on error.
+ */
+int pg_oid2name(const char** name, pg_type_t* table, Oid oid);
+
+/** @} */
+
+#endif /* _PG_OID_H */

+ 76 - 0
modules/db_postgres/pg_res.c

@@ -0,0 +1,76 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file
+ * Functions working with result structures received from PostgreSQL servers.
+ */
+
+#include "pg_res.h"
+#include "pg_cmd.h"
+
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../lib/srdb2/db_gen.h"
+
+
+static void pg_res_free(db_res_t* res, struct pg_res* payload)
+{
+	db_drv_free(&payload->gen);
+	if (payload->res) PQclear(payload->res);
+	pkg_free(payload);
+}
+
+
+int pg_res(db_res_t* res)
+{
+	struct pg_res* pres;
+
+	pres = (struct pg_res*)pkg_malloc(sizeof(struct pg_res));
+	if (pres == NULL) {
+		ERR("postgres: No memory left\n");
+		return -1;
+	}
+	if (db_drv_init(&pres->gen, pg_res_free) < 0) goto error;
+	DB_SET_PAYLOAD(res, pres);
+	return 0;
+	
+ error:
+	if (pres) {
+		db_drv_free(&pres->gen);
+		pkg_free(pres);
+	}
+	return -1;
+}
+
+/** @} */

+ 58 - 0
modules/db_postgres/pg_res.h

@@ -0,0 +1,58 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+#ifndef _PG_RES_H
+#define _PG_RES_H
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file
+ * Data structures and functions to convert results obtained from PostgreSQL
+ * servers.
+ */
+
+#include "../../lib/srdb2/db_drv.h"
+#include "../../lib/srdb2/db_res.h"
+
+#include <libpq-fe.h>
+
+struct pg_res {
+	db_drv_t gen;
+	PGresult* res;
+    int row, rows;
+};
+
+int pg_res(db_res_t* res);
+
+/** @} */
+
+#endif /* _PG_RES_H */

+ 424 - 0
modules/db_postgres/pg_sql.c

@@ -0,0 +1,424 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file
+ * Implementation of various functions that assemble SQL query strings for
+ * PostgreSQL.
+ */
+
+#include "pg_sql.h"
+
+#include "../../lib/srdb2/db_cmd.h"
+#include "../../lib/srdb2/db_fld.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+
+#include <string.h>
+
+
+enum {
+	STR_DELETE,
+	STR_INSERT,
+	STR_UPDATE,
+	STR_SELECT,
+	STR_REPLACE,
+	STR_SET,
+	STR_WHERE,
+	STR_IS,
+	STR_AND,
+	STR_OR,
+	STR_ESC,
+	STR_OP_EQ,
+	STR_OP_NE,
+	STR_OP_LT,
+	STR_OP_GT,
+	STR_OP_LEQ,
+	STR_OP_GEQ,
+	STR_VALUES,
+	STR_FROM,
+	STR_OID,
+	STR_TIMESTAMP,
+	STR_ZT
+};
+
+
+static str strings[] = {
+	STR_STATIC_INIT("delete from "),
+	STR_STATIC_INIT("insert into "),
+	STR_STATIC_INIT("update "),
+	STR_STATIC_INIT("select "),
+	STR_STATIC_INIT("replace "),
+	STR_STATIC_INIT(" set "),
+	STR_STATIC_INIT(" where "),
+	STR_STATIC_INIT(" is "),
+	STR_STATIC_INIT(" and "),
+	STR_STATIC_INIT(" or "),
+	STR_STATIC_INIT("?"),
+	STR_STATIC_INIT("="),
+	STR_STATIC_INIT("!="),
+	STR_STATIC_INIT("<"),
+	STR_STATIC_INIT(">"),
+	STR_STATIC_INIT("<="),
+	STR_STATIC_INIT(">="),
+	STR_STATIC_INIT(") values ("),
+	STR_STATIC_INIT(" from "),
+	STR_STATIC_INIT("select typname,pg_type.oid from pg_type"),
+	STR_STATIC_INIT("select timestamp '2000-01-01 00:00:00' + time '00:00:01'"), 
+	STR_STATIC_INIT("\0")
+};
+
+
+/**
+ * Reallocatable string buffer.
+ */
+struct string_buffer {
+	char *s;			/**< allocated memory itself */
+	int   len;			/**< used memory */
+	int   size;			/**< total size of allocated memory */
+	int   increment;	/**< increment when realloc is necessary */ 
+};
+
+
+/** Appends string to string buffer.
+ * This function appends string to dynamically created string buffer,
+ * the buffer is automatically extended if there is not enough room
+ * in the buffer. The buffer is allocated using pkg_malloc.
+ * @param sb    string buffer
+ * @param nstr  string to add
+ * @return      0 if OK, -1 if failed
+ */
+static inline int sb_add(struct string_buffer *sb, str *nstr)
+{
+	int new_size = 0;
+	int rsize = sb->len + nstr->len;
+	int asize;
+	char *newp;
+	
+	if (rsize > sb->size) {
+		asize = rsize - sb->size;
+		new_size = sb->size + (asize / sb->increment  + 
+							   (asize % sb->increment > 0)) * sb->increment;
+		newp = pkg_malloc(new_size);
+		if (!newp) {
+			ERR("postgres: No memory left\n");
+			return -1;
+		}
+		if (sb->s) {
+			memcpy(newp, sb->s, sb->len);
+			pkg_free(sb->s);
+		}
+		sb->s = newp;
+		sb->size = new_size;
+	}
+	memcpy(sb->s + sb->len, nstr->s, nstr->len);
+	sb->len += nstr->len;
+	return 0;
+}
+
+
+/** Creates str string from zero terminated string without copying.
+ * This function initializes members of a temporary str structure
+ * with the pointer and lenght of the string from s parameter.
+ *
+ * @param str A pointer to temporary str structure.
+ * @param s   A zero terminated string.
+ * @return Pointer to the str structure.
+ */
+static inline str* set_str(str *str, const char *s)
+{
+	str->s = (char *)s;
+	str->len = strlen(s);
+	return str;
+}
+
+
+/** Returns a parameter marker for PostgreSQL with number i
+ * The function builds a parameter marker for use in
+ * PostgreSQL SQL queries, such as $1, $2, etc.
+ * @param i Number of the parameter
+ * @retval A pointer to static string with the marker
+ */
+static str* get_marker(unsigned int i)
+{
+	static char buf[INT2STR_MAX_LEN + 1];
+	static str res;
+	const char* c;
+
+	buf[0] = '$';
+	res.s = buf;
+
+	c = int2str(i, &res.len);
+	memcpy(res.s + 1, c, res.len);
+	res.len++;
+	return &res;
+}
+
+
+int build_update_sql(str* sql_cmd, db_cmd_t* cmd)
+{
+	struct string_buffer sql_buf = {.s = NULL, .len = 0, 
+							  .size = 0, .increment = 128};
+	db_fld_t* fld;
+	int i, rv = 0;
+	str tmpstr;
+
+	rv = sb_add(&sql_buf, &strings[STR_UPDATE]); /* "UPDATE " */
+	rv |= sb_add(&sql_buf, set_str(&tmpstr, "\""));
+	rv |= sb_add(&sql_buf, &cmd->table);		 /* table name */
+	rv |= sb_add(&sql_buf, set_str(&tmpstr, "\""));
+	rv |= sb_add(&sql_buf, &strings[STR_SET]);	 /* " SET " */
+
+	/* column name-value pairs */
+	for(i = 0, fld = cmd->vals; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[i]); i++) {
+		rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].name));
+		rv |= sb_add(&sql_buf, set_str(&tmpstr, "="));
+		rv |= sb_add(&sql_buf, &strings[STR_ESC]);
+		if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, set_str(&tmpstr, ","));
+	}
+	if (rv) goto error;
+
+	if (!DB_FLD_EMPTY(cmd->match)) {
+		rv |= sb_add(&sql_buf, &strings[STR_WHERE]);
+
+		for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
+			rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].name));
+
+			switch(fld[i].op) {
+			case DB_EQ:  rv |= sb_add(&sql_buf, &strings[STR_OP_EQ]);  break;
+			case DB_NE:  rv |= sb_add(&sql_buf, &strings[STR_OP_NE]);  break;
+			case DB_LT:  rv |= sb_add(&sql_buf, &strings[STR_OP_LT]);  break;
+			case DB_GT:  rv |= sb_add(&sql_buf, &strings[STR_OP_GT]);  break;
+			case DB_LEQ: rv |= sb_add(&sql_buf, &strings[STR_OP_LEQ]); break;
+			case DB_GEQ: rv |= sb_add(&sql_buf, &strings[STR_OP_GEQ]); break;
+			}
+			
+			rv |= sb_add(&sql_buf, get_marker(i + 1));
+			if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, &strings[STR_AND]);
+		}
+	}
+	rv |= sb_add(&sql_buf, &strings[STR_ZT]);
+	if (rv) goto error;
+
+	sql_cmd->s = sql_buf.s;
+	sql_cmd->len = sql_buf.len;
+	return 0;
+
+error:
+	if (sql_buf.s) pkg_free(sql_buf.s);
+	return -1;
+}
+
+
+int build_insert_sql(str* sql_cmd, db_cmd_t* cmd)
+{
+	struct string_buffer sql_buf = {.s = NULL, .len = 0, 
+									.size = 0, .increment = 128};
+	db_fld_t* fld;
+	int i, rv = 0;
+	str tmpstr;
+
+	rv = sb_add(&sql_buf, &strings[STR_INSERT]); /* "INSERT INTO " */
+	rv |= sb_add(&sql_buf, set_str(&tmpstr, "\""));
+	rv |= sb_add(&sql_buf, &cmd->table);		 /* table name */
+	rv |= sb_add(&sql_buf, set_str(&tmpstr, "\" ("));
+
+	/* column names */
+	for(i = 0, fld = cmd->vals; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[i]); i++) {
+		rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].name));
+		if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, set_str(&tmpstr, ","));
+	}
+	if (rv) goto error;
+
+	rv |= sb_add(&sql_buf, &strings[STR_VALUES]);
+
+	for(i = 0, fld = cmd->vals; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[i]); i++) {
+		rv |= sb_add(&sql_buf, get_marker(i + 1));
+		if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, set_str(&tmpstr, ","));
+	}
+	rv |= sb_add(&sql_buf, set_str(&tmpstr, ")"));
+	rv |= sb_add(&sql_buf, &strings[STR_ZT]);
+	if (rv) goto error;
+				 
+	sql_cmd->s = sql_buf.s;
+	sql_cmd->len = sql_buf.len;
+	return 0;
+
+error:
+	if (sql_buf.s) pkg_free(sql_buf.s);
+	return -1;
+}
+
+
+int build_delete_sql(str* sql_cmd, db_cmd_t* cmd)
+{
+	struct string_buffer sql_buf = {.s = NULL, .len = 0, 
+									.size = 0, .increment = 128};
+	db_fld_t* fld;
+	int i, rv = 0;
+	str tmpstr;
+
+	rv = sb_add(&sql_buf, &strings[STR_DELETE]); /* "DELETE FROM " */
+	rv |= sb_add(&sql_buf, set_str(&tmpstr, "\""));
+	rv |= sb_add(&sql_buf, &cmd->table);		 /* table name */
+	rv |= sb_add(&sql_buf, set_str(&tmpstr, "\""));
+
+	if (!DB_FLD_EMPTY(cmd->match)) {
+		rv |= sb_add(&sql_buf, &strings[STR_WHERE]);
+
+		for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
+			rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].name));
+
+			switch(fld[i].op) {
+			case DB_EQ:  rv |= sb_add(&sql_buf, &strings[STR_OP_EQ]);  break;
+			case DB_NE:  rv |= sb_add(&sql_buf, &strings[STR_OP_NE]);  break;
+			case DB_LT:  rv |= sb_add(&sql_buf, &strings[STR_OP_LT]);  break;
+			case DB_GT:  rv |= sb_add(&sql_buf, &strings[STR_OP_GT]);  break;
+			case DB_LEQ: rv |= sb_add(&sql_buf, &strings[STR_OP_LEQ]); break;
+			case DB_GEQ: rv |= sb_add(&sql_buf, &strings[STR_OP_GEQ]); break;
+			}
+			
+			rv |= sb_add(&sql_buf, get_marker(i + 1));
+			if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, &strings[STR_AND]);
+		}
+	}
+	rv |= sb_add(&sql_buf, &strings[STR_ZT]);
+	if (rv) goto error;
+
+	sql_cmd->s = sql_buf.s;
+	sql_cmd->len = sql_buf.len;
+	return 0;
+
+error:
+	if (sql_buf.s) pkg_free(sql_buf.s);
+	return -1;
+}
+
+
+int build_select_sql(str* sql_cmd, db_cmd_t* cmd)
+{
+	struct string_buffer sql_buf = {.s = NULL, .len = 0, 
+									.size = 0, .increment = 128};
+	db_fld_t* fld;
+	int i, rv = 0;
+	str tmpstr;
+
+	rv = sb_add(&sql_buf, &strings[STR_SELECT]); /* "SELECT " */
+
+	if (DB_FLD_EMPTY(cmd->result)) {
+		rv |= sb_add(&sql_buf, set_str(&tmpstr, "*"));
+	} else {
+		for(i = 0, fld = cmd->result; !DB_FLD_LAST(fld[i]); i++) {
+			rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].name));
+			if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, set_str(&tmpstr, ","));
+		}
+	}
+
+	rv |= sb_add(&sql_buf, &strings[STR_FROM]);  /* " FROM " */
+	rv |= sb_add(&sql_buf, set_str(&tmpstr, "\""));
+	rv |= sb_add(&sql_buf, &cmd->table);		 /* table name */
+	rv |= sb_add(&sql_buf, set_str(&tmpstr, "\""));
+
+	if (!DB_FLD_EMPTY(cmd->match)) {
+		rv |= sb_add(&sql_buf, &strings[STR_WHERE]);
+
+		for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
+			rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].name));
+
+			switch(fld[i].op) {
+			case DB_EQ:  rv |= sb_add(&sql_buf, &strings[STR_OP_EQ]);  break;
+			case DB_NE:  rv |= sb_add(&sql_buf, &strings[STR_OP_NE]);  break;
+			case DB_LT:  rv |= sb_add(&sql_buf, &strings[STR_OP_LT]);  break;
+			case DB_GT:  rv |= sb_add(&sql_buf, &strings[STR_OP_GT]);  break;
+			case DB_LEQ: rv |= sb_add(&sql_buf, &strings[STR_OP_LEQ]); break;
+			case DB_GEQ: rv |= sb_add(&sql_buf, &strings[STR_OP_GEQ]); break;
+			}
+			
+			rv |= sb_add(&sql_buf, get_marker(i + 1));
+			if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, &strings[STR_AND]);
+		}
+	}
+	rv |= sb_add(&sql_buf, &strings[STR_ZT]);
+	if (rv) goto error;
+
+	sql_cmd->s = sql_buf.s;
+	sql_cmd->len = sql_buf.len;
+	return 0;
+
+error:
+	if (sql_buf.s) pkg_free(sql_buf.s);
+	return -1;
+}
+
+
+int build_select_oid_sql(str* sql_cmd)
+{
+	struct string_buffer sql_buf = {.s = NULL, .len = 0, 
+									.size = 0, .increment = 128};
+	int rv = 0;
+	
+	rv = sb_add(&sql_buf, &strings[STR_OID]);
+	rv |= sb_add(&sql_buf, &strings[STR_ZT]);
+	if (rv) goto error;
+
+	sql_cmd->s = sql_buf.s;
+	sql_cmd->len = sql_buf.len;
+	return 0;
+
+ error:
+	if (sql_buf.s) pkg_free(sql_buf.s);
+	return -1;
+}
+
+
+int build_timestamp_format_sql(str* sql_cmd)
+{
+	struct string_buffer sql_buf = {.s = NULL, .len = 0, 
+									.size = 0, .increment = 128};
+	int rv = 0;
+	
+	rv = sb_add(&sql_buf, &strings[STR_TIMESTAMP]);
+	rv |= sb_add(&sql_buf, &strings[STR_ZT]);
+	if (rv) goto error;
+
+	sql_cmd->s = sql_buf.s;
+	sql_cmd->len = sql_buf.len;
+	return 0;
+
+ error:
+	if (sql_buf.s) pkg_free(sql_buf.s);
+	return -1;
+}
+
+/** @} */

+ 118 - 0
modules/db_postgres/pg_sql.h

@@ -0,0 +1,118 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+#ifndef _PG_SQL_H
+#define _PG_SQL_H
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file
+ * Implementation of various functions that assemble SQL query strings for
+ * PostgreSQL.
+ */
+
+#include "../../lib/srdb2/db_cmd.h"
+#include "../../str.h"
+
+
+/** Builds an UPDATE SQL statement.
+ * This function creates an UPDATE SQL statement where column values are
+ * replaced with special markers. The resulting SQL statement is suitable
+ * for submitting as prepared statement. The result string is zero terminated.
+ * @param sql_cmd Pointer to a str variable where the resulting SQL statement
+ *                will be stored. The buffer in sql_cmd->s is allocated using
+ *                pkg_malloc and the caller of the function is responsible for
+ *                freeing it.
+ * @param cmd The command whose data will be used to generate the query.
+ * @return 0 on success, negative number on error
+ */
+int build_update_sql(str* sql_cmd, db_cmd_t* cmd);
+
+
+/** Builds an INSERT SQL statement.
+ * This function creates an INSERT SQL statement where column values are
+ * replaced with special markers. The resulting SQL statement is suitable
+ * for submitting as prepared statement. The result string is zero terminated.
+ * @param sql_cmd Pointer to a str variable where the resulting SQL statement
+ *                will be stored. The buffer in sql_cmd->s is allocated using
+ *                pkg_malloc and the caller of the function is responsible for *                freeing it.
+ * @param cmd The command whose data will be used to generate the query.
+ * @return 0 on success, negative number on error
+ */
+int build_insert_sql(str* sql_cmd, db_cmd_t* cmd);
+
+
+/** Builds a DELETE SQL statement.
+ * This function creates a DELETE SQL statement where column values are
+ * replaced with special markers. The resulting SQL statement is suitable
+ * for submitting as prepared statement. The result string is zero terminated.
+ * @param sql_cmd Pointer to a str variable where the resulting SQL statement
+ *                will be stored. The buffer in sql_cmd->s is allocated using
+ *                pkg_malloc and the caller of the function is responsible for
+ *                freeing it.
+ * @param cmd The command whose data will be used to generate the query.
+ * @return 0 on success, negative number on error
+ */
+int build_delete_sql(str* sql_cmd, db_cmd_t* cmd);
+
+
+/** Builds a SELECT SQL statement.
+ * This function creates a SELECT SQL statement where column values are
+ * replaced with special markers. The resulting SQL statement is suitable
+ * for submitting as prepared statement. The result string is zero terminated.
+ * @param sql_cmd Pointer to a str variable where the resulting SQL statement
+ *                will be stored. The buffer in sql_cmd->s is allocated using
+ *                pkg_malloc and the caller of the function is responsible for
+ *                freeing it.
+ * @param cmd The command whose data will be used to generate the query.
+ * @return 0 on success, negative number on error
+ */
+int build_select_sql(str* sql_cmd, db_cmd_t* cmd);
+
+
+/* Builds SQL query used to obtain the list of supported field types.
+ * This function builds a special SQL query that is used to obtain the list
+ * of supported field type from the server's system catalogs.
+ */
+int build_select_oid_sql(str* sql_cmd);
+
+
+/** Builds the SQL query used to determine the format of timestamp fields.
+ * This function builds a special SQL query that is sent to the server
+ * immediately after establishing a connection to determine the format of
+ * timestamp fields used on the server.
+ */
+int build_timestamp_format_sql(str* sql_cmd);
+
+/** @} */
+
+#endif /* _PG_SQL_H */

+ 298 - 0
modules/db_postgres/pg_uri.c

@@ -0,0 +1,298 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file 
+ * The implementation of parser parsing postgres://.. URIs.
+ */
+
+#include "pg_uri.h"
+
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "../../ut.h"
+#include "../../lib/srdb2/db_gen.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+
+/** compare s1 & s2  with a function f (which should return 0 if ==);
+ * s1 & s2 can be null
+ * return 0 if match, 1 if not 
+ */
+#define cmpstr(s1, s2, f) \
+	((s1)!=(s2)) && ((s1)==0 || (s2)==0 || (f)((s1), (s2))!=0)
+
+
+/** Compare two connection URIs */
+static unsigned char pg_uri_cmp(db_uri_t* uri1, db_uri_t* uri2)
+{
+	struct pg_uri* puri1, *puri2;
+
+	if (!uri1 || !uri2) return 0;
+
+	puri1 = DB_GET_PAYLOAD(uri1);
+	puri2 = DB_GET_PAYLOAD(uri2);
+	if (puri1->port != puri2->port) return 0;
+
+	if (cmpstr(puri1->username, puri2->username, strcmp)) return 0;
+	if (cmpstr(puri1->password, puri2->password, strcmp)) return 0;
+	if (cmpstr(puri1->host, puri2->host, strcasecmp)) return 0;
+	if (cmpstr(puri1->database, puri2->database, strcmp)) return 0;
+	return 1;
+}
+
+
+/** Duplicate a string
+ */
+static int dupl_string(char** dst, const char* begin, const char* end)
+{
+	if (*dst) pkg_free(*dst);
+
+	*dst = pkg_malloc(end - begin + 1);
+	if ((*dst) == NULL) {
+		return -1;
+	}
+
+	memcpy(*dst, begin, end - begin);
+	(*dst)[end - begin] = '\0';
+	return 0;
+}
+
+
+/** Parses postgres URI of form 
+ * //[username[:password]@]hostname[:port]/database
+ *
+ * Returns 0 if parsing was successful and -1 otherwise
+ */
+static int parse_postgres_uri(struct pg_uri* res, str* uri)
+{
+#define SHORTEST_DB_URL "//a/b"
+#define SHORTEST_DB_URL_LEN (sizeof(SHORTEST_DB_URL) - 1)
+
+	enum state {
+		ST_SLASH1,     /* First slash */
+		ST_SLASH2,     /* Second slash */
+		ST_USER_HOST,  /* Username or hostname */
+		ST_PASS_PORT,  /* Password or port part */
+		ST_HOST,       /* Hostname part */
+		ST_PORT,       /* Port part */
+		ST_DB          /* Database part */
+	};
+
+	enum state st;
+	int  i;
+	const char* begin;
+	char* prev_token;
+
+	prev_token = 0;
+
+	if (!res || !res) {
+		goto err;
+	}
+	
+	if (uri->len < SHORTEST_DB_URL_LEN) {
+		goto err;
+	}
+	
+	st = ST_SLASH1;
+	begin = uri->s;
+
+	for(i = 0; i < uri->len; i++) {
+		switch(st) {
+		case ST_SLASH1:
+			switch(uri->s[i]) {
+			case '/':
+				st = ST_SLASH2;
+				break;
+
+			default:
+				goto err;
+			}
+			break;
+
+		case ST_SLASH2:
+			switch(uri->s[i]) {
+			case '/':
+				st = ST_USER_HOST;
+				begin = uri->s + i + 1;
+				break;
+				
+			default:
+				goto err;
+			}
+			break;
+
+		case ST_USER_HOST:
+			switch(uri->s[i]) {
+			case '@':
+				st = ST_HOST;
+				if (dupl_string(&res->username, begin, uri->s + i) < 0) goto err;
+				begin = uri->s + i + 1;
+				break;
+
+			case ':':
+				st = ST_PASS_PORT;
+				if (dupl_string(&prev_token, begin, uri->s + i) < 0) goto err;
+				begin = uri->s + i + 1;
+				break;
+
+			case '/':
+				if (memchr(uri->s + i + 1, '/', uri->len - i - 1) != NULL)
+					break;
+				if (dupl_string(&res->host, begin, uri->s + i) < 0) goto err;
+				if (dupl_string(&res->database, uri->s + i + 1, uri->s + uri->len) < 0) 
+					goto err;
+				return 0;
+			}
+			break;
+
+		case ST_PASS_PORT:
+			switch(uri->s[i]) {
+			case '@':
+				st = ST_HOST;
+				res->username = prev_token;
+				if (dupl_string(&res->password, begin, uri->s + i) < 0) goto err;
+				begin = uri->s + i + 1;
+				break;
+
+			case '/':
+				if (memchr(uri->s + i + 1, '/', uri->len - i - 1) != NULL)
+					break;
+				res->host = prev_token;
+				res->port = str2s(begin, uri->s + i - begin, 0);
+				if (dupl_string(&res->database, uri->s + i + 1, uri->s + uri->len) < 0) 
+					goto err;
+				return 0;
+			}
+			break;
+
+		case ST_HOST:
+			switch(uri->s[i]) {
+			case ':':
+				st = ST_PORT;
+				if (dupl_string(&res->host, begin, uri->s + i) < 0) goto err;
+				begin = uri->s + i + 1;
+				break;
+
+			case '/':
+				if (memchr(uri->s + i + 1, '/', uri->len - i - 1) != NULL)
+					break;
+				if (dupl_string(&res->host, begin, uri->s + i) < 0) goto err;
+				if (dupl_string(&res->database, uri->s + i + 1, uri->s + uri->len) < 0) 
+					goto err;
+				return 0;
+			}
+			break;
+
+		case ST_PORT:
+			switch(uri->s[i]) {
+			case '/':
+				res->port = str2s(begin, uri->s + i - begin, 0);
+				if (dupl_string(&res->database, uri->s + i + 1, uri->s + uri->len) < 0) 
+					goto err;
+				return 0;
+			}
+			break;
+			
+		case ST_DB:
+			break;
+		}
+	}
+
+	if (st != ST_DB) goto err;
+	return 0;
+
+ err:
+	if (prev_token) pkg_free(prev_token);
+	if (res == NULL) return -1;
+	if (res->username) {
+		pkg_free(res->username);
+		res->username = NULL;
+	}
+	if (res->password) {
+		pkg_free(res->password);
+		res->password = NULL;
+	}
+	if (res->host) {
+		pkg_free(res->host);
+		res->host = NULL;
+	}
+	if (res->database) {
+		pkg_free(res->database);
+		res->database = NULL;
+	}
+	return -1;
+}
+
+
+
+static void pg_uri_free(db_uri_t* uri, struct pg_uri* payload)
+{
+	if (payload == NULL) return;
+	db_drv_free(&payload->drv);
+	if (payload->username) pkg_free(payload->username);
+	if (payload->password) pkg_free(payload->password);
+	if (payload->host) pkg_free(payload->host);
+	if (payload->database) pkg_free(payload->database);
+	pkg_free(payload);
+}
+
+
+int pg_uri(db_uri_t* uri)
+{
+	struct pg_uri* puri;
+
+	puri = (struct pg_uri*)pkg_malloc(sizeof(struct pg_uri));
+	if (puri == NULL) {
+		ERR("postgres: No memory left\n");
+		goto error;
+	}
+	memset(puri, '\0', sizeof(struct pg_uri));
+	if (db_drv_init(&puri->drv, pg_uri_free) < 0) goto error;
+	if (parse_postgres_uri(puri, &uri->body) < 0) goto error;
+
+	DB_SET_PAYLOAD(uri, puri);
+	uri->cmp = pg_uri_cmp;
+	return 0;
+
+ error:
+	if (puri) {
+		db_drv_free(&puri->drv);
+		if (puri) pkg_free(puri);
+	}
+	return -1;
+}
+
+/** @} */

+ 71 - 0
modules/db_postgres/pg_uri.h

@@ -0,0 +1,71 @@
+/* 
+ * $Id$ 
+ *
+ * PostgreSQL Database Driver for SER
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005-2008 iptelorg GmbH
+ *
+ * This file is part of SER, a free SIP server.
+ *
+ * SER 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
+ *
+ * For a license to use the ser software under conditions other than those
+ * described here, or to purchase support for this software, please contact
+ * iptel.org by e-mail at the following addresses: [email protected]
+ *
+ * SER 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
+ */
+
+#ifndef _PG_URI_H
+#define _PG_URI_H
+
+/** \addtogroup postgres
+ * @{ 
+ */
+
+/** \file 
+ * The implementation of parser parsing postgres://.. URIs.
+ */
+
+#include "../../lib/srdb2/db_uri.h"
+#include "../../lib/srdb2/db_drv.h"
+
+/** PostgreSQL driver specific payload to attach to db_uri structures.
+ * This is the PostgreSQL specific structure that will be attached
+ * to generic db_uri structures in the database API in SER. The 
+ * structure contains parsed elements of postgres:// uri.
+ */
+struct pg_uri {
+	db_drv_t drv;
+	char* username;
+	char* password;
+	char* host;
+	unsigned short port;
+	char* database;
+};
+
+/** Create a new pg_uri structure and parse the URI in parameter.
+ * This function builds a new pg_uri structure from the body of
+ * the generic URI given to it in parameter.
+ * @param uri A generic db_uri structure.
+ * @retval 0 on success
+ * @retval A negative number on error.
+ */
+int pg_uri(db_uri_t* uri);
+
+/** @} */
+
+#endif /* _PG_URI_H */
+

+ 107 - 0
scripts/kam_to_sr.sh

@@ -0,0 +1,107 @@
+#!/usr/bin/env sh
+#
+# This is a simple script which attempts to convert kamailio modules so that
+# they can be used with the sip-router core. Most of the changes done by the
+# script deal with the changes in the database abstraction layer in the
+# sip-router source tree. 
+#
+# Run this script in module directory to convert it from kamailio core to
+# sip-router core. The root of the tree should be two levels up, otherwise
+# relative paths to headers (../..) would not work and the module will not
+# compile.
+# 
+# Some of the changes done by the script:
+#
+#  * Extra defines in the Makefile to make the module link with libsrdb1
+#  * Path to database headers updated to point to lib/srdb1
+#  * db_con_t and db_res_t renamed to db1_con_t and db1_res_t in *.[ch]
+#  * Value type names such as DB_INT changed to DB1_INT in *.[ch]
+#
+# NOTE: There is no guarantee that the update module would compile or even
+#       work. Make a backup before running the script. You have been warned!
+# 
+# Written by Jan Janak <[email protected]>
+#
+
+if [ ! -f Makefile ] ; then
+	echo "ERROR: Could not find module Makefile"
+	echo "       Run this file in the module directory"
+	exit 1
+fi
+
+if ! egrep "Makefile\.modules" Makefile >/dev/null ; then
+	echo "ERROR: Doesn't look like a module..."
+	exit 1
+fi
+
+if ! egrep '^#[ \t]*include[ \t]*".*\/db\/db(_(cap|con|id|key|op|pool|query|res|row|ut|val))?\.h[ \t]*"' *.[ch] >/dev/null ; then
+	echo "The module does not seem to include old database headers..."
+	exit 0
+fi
+
+echo -n "Updating Makefile..."
+cp Makefile Makefile.backup
+cat Makefile.backup | gawk '
+BEGIN {
+    serlibpath_seen = 0
+    libs_seen = 0
+    defs_seen = 0
+}
+
+# If the define already exists then skip it, this ensures that
+# we do not add the same line more than once.
+/^[ \t]*DEFS[ \t]*\+?=.*OPENSER_MOD_INTERFACE/ {
+    defs_seen = 1
+}
+
+/^[ \t]*SER_LIBS[ \t]*\+?=.*srdb1\/srdb1/ {
+    libs_seen = 1
+}
+
+/^[ \t]*SERLIBPATH[ \t]*=/ {
+    serlibpath_seen = 1
+}
+
+# Write everything just before the line including Makefile.modules,
+# this is most likely the last line in the Makefile
+/^[ \t]*include[ \t]+.*\/Makefile\.modules[ \t]*$/ {
+    if (serlibpath_seen == 0) print "SERLIBPATH=../../lib"
+    if (defs_seen == 0) print "DEFS+=-DOPENSER_MOD_INTERFACE"
+    if (libs_seen == 0) print "SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1"
+}
+
+{ print $0 }
+
+' > Makefile
+echo "done."
+
+for file in *.[ch] ; do
+	echo -n "Updating file $file..."
+	cp $file $file.backup
+	cat $file.backup | gawk '
+
+/^#[ \t]*include[ \t]*".*\/db\/db(_(cap|con|id|key|op|pool|query|res|row|ut|val))?\.h[ \t]*"/ {
+    sub("/db/", "/lib/srdb1/", $0);
+}
+
+/(^|[^a-zA-Z0-9_])(db_(con|res)_t|struct[ \t]+db_(con|res))([^a-zA-Z0-9_]|$)/ {
+    gsub("struct[ \t]+db_con", "struct db1_con", $0);
+    gsub("struct[ \t]+db_res", "struct db1_res", $0);
+    gsub("db_con_t", "db1_con_t", $0);
+    gsub("db_res_t", "db1_res_t", $0);
+}
+
+/(^|[^a-zA-Z0-9_])DB_((BIG)?INT|DOUBLE|STR(ING)?|DATETIME|BLOB|BITMAP)([^a-zA-Z0-9_]|$)/ {
+    gsub("DB_INT", "DB1_INT", $0);
+    gsub("DB_BIGINT", "DB1_BIGINT", $0);
+    gsub("DB_DOUBLE", "DB1_DOUBLE", $0);
+    gsub("DB_STR", "DB1_STR", $0);
+    gsub("DB_DATETIME", "DB1_DATETIME", $0);
+    gsub("DB_BLOB", "DB1_BLOB", $0);
+    gsub("DB_BITMAP", "DB1_BITMAP", $0);
+}
+
+{ print $0 }
+' >$file
+	echo "done."
+done

+ 107 - 0
scripts/ser_to_sr.sh

@@ -0,0 +1,107 @@
+#!/usr/bin/env sh
+#
+# This is a simple script which attempts to convert ser modules so that they
+# can be used with the sip-router core. Most of the changes done by the script
+# deal with the changes in the database abstraction layer in the sip-router
+# source tree.
+#
+# Run this script in module directory to convert it from ser core to
+# sip-router core. The root of the tree should be two levels up, otherwise
+# relative paths to headers (../..) would not work and the module will not
+# compile.
+# 
+# Some of the changes done by the script:
+#
+#  * Extra defines in the Makefile to make the module link with libsrdb2
+#  * Path to database headers updated to point to lib/srdb2
+#  * Database flag names renamed from DB_* to SRDB_* 
+#    (DB_DISABLED -> SRDB_DISABLED)
+#
+# NOTE: There is no guarantee that the update module would compile or even
+#       work. Make a backup before running the script. You have been warned!
+# 
+# Written by Jan Janak <[email protected]>
+#
+
+if [ ! -f Makefile ] ; then
+	echo "ERROR: Could not find module Makefile"
+	echo "       Run this file in the module directory"
+	exit 1
+fi
+
+if ! egrep "Makefile\.modules" Makefile >/dev/null ; then
+	echo "ERROR: Doesn't look like a module..."
+	exit 1
+fi
+
+if ! egrep '^#[ \t]*include[ \t]*".*\/db\/db(_(cmd|con|ctx|drv|fld|gen|pool|rec|res|uri))?\.h[ \t]*"' *.[ch] >/dev/null ; then
+	echo "The module does not seem to include old database headers..."
+	exit 0
+fi
+
+echo -n "Updating Makefile..."
+cp Makefile Makefile.backup
+cat Makefile.backup | gawk '
+BEGIN {
+    serlibpath_seen = 0
+    libs_seen = 0
+    defs_seen = 0
+}
+
+# If the define already exists then skip it, this ensures that
+# we do not add the same line more than once.
+/^[ \t]*DEFS[ \t]*\+?=.*SER_MOD_INTERFACE/ {
+    defs_seen = 1
+}
+
+/^[ \t]*SER_LIBS[ \t]*\+?=.*srdb2\/srdb2/ {
+    libs_seen = 1
+}
+
+/^[ \t]*SERLIBPATH[ \t]*=/ {
+    serlibpath_seen = 1
+}
+
+# Write everything just before the line including Makefile.modules,
+# this is most likely the last line in the Makefile
+/^[ \t]*include[ \t]+.*\/Makefile\.modules[ \t]*$/ {
+    if (serlibpath_seen == 0) print "SERLIBPATH=../../lib"
+    if (defs_seen == 0) print "DEFS+=-DSER_MOD_INTERFACE"
+    if (libs_seen == 0) print "SER_LIBS+=$(SERLIBPATH)/srdb2/srdb2"
+}
+
+{ print $0 }
+
+' > Makefile
+echo "done."
+
+for file in *.[ch] ; do
+	echo -n "Updating file $file..."
+	cp $file $file.backup
+	cat $file.backup | gawk '
+
+/^#[ \t]*include[ \t]*".*\/db\/db(_(cmd|con|ctx|drv|fld|gen|pool|rec|res|uri))?\.h[ \t]*"/ {
+    sub("/db/", "/lib/srdb2/", $0);
+}
+
+/(^|[^a-zA-Z0-9_])DB_(LOAD_SER|DISABLED|CANON|IS_(TO|FROM)|FOR_SERWEB|PENDING|((CALLER|CALLEE)_)?DELETED|MULTIVALUE|FILL_ON_REG|REQUIRED|DIR)([^a-zA-Z0-9_]|$)/ {
+    gsub("DB_LOAD_SER", "SRDB_LOAD_SER", $0);
+    gsub("DB_DISABLED", "SRDB_DISABLED", $0);
+    gsub("DB_CANON", "SRDB_CANON", $0);
+    gsub("DB_IS_TO", "SRDB_IS_TO", $0);
+    gsub("DB_IS_FROM", "SRDB_IS_FROM", $0);
+    gsub("DB_FOR_SERWEB", "SRDB_FOR_SERWEB", $0);
+    gsub("DB_PENDING", "SRDB_PENDING", $0);
+    gsub("DB_DELETED", "SRDB_DELETED", $0);
+    gsub("DB_CALLER_DELETED", "SRDB_CALLER_DELETED", $0);
+    gsub("DB_CALLEE_DELETED", "SRDB_CALLEE_DELETED", $0);
+    gsub("DB_MULTIVALUE", "SRDB_MULTIVALUE", $0);
+    gsub("DB_FILL_ON_REG", "SRDB_FILL_ON_REG", $0);
+    gsub("DB_REQUIRED", "SRDB_REQUIRED", $0);
+    gsub("DB_DIR", "SRDB_DIR", $0);
+}
+
+{ print $0 }
+' >$file
+	echo "done."
+done