Explorar el Código

- rename database modules to use the 'db_' prefix

git-svn-id: https://openser.svn.sourceforge.net/svnroot/openser/trunk@3638 689a6050-402a-0410-94f2-e92a70836424
Henning Westerholt hace 17 años
commit
0761b7c23f

+ 36 - 0
modules/db_postgres/km_Makefile

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

+ 106 - 0
modules/db_postgres/km_README

@@ -0,0 +1,106 @@
+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. OpenSER Modules
+              1.2.2. External Libraries or Applications
+
+        1.3. Exported Parameters
+        1.4. Exported Functions
+        1.5. Installation & Running
+
+   2. Developer's Guide
+   3. Frequently Asked Questions
+     __________________________________________________________
+
+Chapter 1. User's Guide
+
+1.1. Overview
+
+   Module description
+     __________________________________________________________
+
+1.2. Dependencies
+
+1.2.1. OpenSER Modules
+
+   The following modules must be loaded before this module:
+
+     * No dependencies on other OpenSER modules.
+     __________________________________________________________
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed
+   before running OpenSER with this module loaded:
+
+     * None.
+     __________________________________________________________
+
+1.3. Exported Parameters
+
+   NONE
+     __________________________________________________________
+
+1.4. Exported Functions
+
+   NONE
+     __________________________________________________________
+
+1.5. Installation & Running
+
+   Notes about installation and running.
+     __________________________________________________________
+
+Chapter 2. Developer's Guide
+
+   The module does not provide any API to use in other OpenSER
+   modules.
+     __________________________________________________________
+
+Chapter 3. Frequently Asked Questions
+
+   3.1. Where can I find more about OpenSER?
+   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 OpenSER?
+
+   Take a look at http://openser.org/.
+
+   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:
+
+     * User Mailing List -
+       http://openser.org/cgi-bin/mailman/listinfo/users
+     * Developer Mailing List -
+       http://openser.org/cgi-bin/mailman/listinfo/devel
+
+   E-mails regarding any stable OpenSER release should be sent to
+   <[email protected]> and e-mails regarding development versions
+   should be sent to <[email protected]>.
+
+   If you want to keep the mail private, send it to
+   <[email protected]>.
+
+   3.3. How can I report a bug?
+
+   Please follow the guidelines provided at:
+   http://sourceforge.net/tracker/?group_id=139143.

+ 81 - 0
modules/db_postgres/km_db_mod.c

@@ -0,0 +1,81 @@
+/* 
+ * $Id$ 
+ *
+ * Postgres module interface
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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-03-11  updated to the new module exports interface (andrei)
+ *  2003-03-16  flags export parameter added (janakj)
+ */
+
+#include <stdio.h>
+#include "../../sr_module.h"
+#include "../../db/db_con.h"
+#include "dbase.h"
+
+MODULE_VERSION
+
+static int mod_init(void);
+
+/*
+ * PostgreSQL database module interface
+ */
+
+static cmd_export_t cmds[]={
+	{"db_use_table",    (cmd_function)db_postgres_use_table,    2, 0, 0, 0},
+	{"db_init",         (cmd_function)db_postgres_init,         1, 0, 0, 0},
+	{"db_close",        (cmd_function)db_postgres_close,        2, 0, 0, 0},
+	{"db_query",        (cmd_function)db_postgres_query,        2, 0, 0, 0},
+	{"db_fetch_result", (cmd_function)db_postgres_fetch_result, 2, 0, 0, 0},
+	{"db_raw_query",    (cmd_function)db_postgres_raw_query,    2, 0, 0, 0},
+	{"db_free_result",  (cmd_function)db_postgres_free_query,   2, 0, 0, 0},
+	{"db_insert",       (cmd_function)db_postgres_insert,       2, 0, 0, 0},
+	{"db_delete",       (cmd_function)db_postgres_delete,       2, 0, 0, 0},
+	{"db_update",       (cmd_function)db_postgres_update,       2, 0, 0, 0},
+	{0,0,0,0,0,0}
+};
+
+
+
+struct module_exports exports = {	
+	"postgres",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	0,   /*  module parameters */
+	0,   /* exported statistics */
+	0,   /* exported MI functions */
+	0,        /* exported pseudo-variables */
+	0,        /* extra processes */
+	mod_init, /* module initialization function */
+	0,        /* response function*/
+	0,        /* destroy function */
+	0         /* per-child init function */
+};
+
+
+static int mod_init(void)
+{
+	LM_INFO("initializing...\n");
+	return 0;
+}
+

+ 383 - 0
modules/db_postgres/km_db_res.c

@@ -0,0 +1,383 @@
+/*
+ * $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) 2006 Norman Brandinger
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ---
+ *
+ * History
+ * -------
+ * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
+ *
+ * 2006-07-26 added BPCHAROID as a valid type for DB_STRING conversions
+ *            this removes the "unknown type 1042" log messages (norm)
+ *
+ * 2006-10-27 Added fetch support (norm)
+ *            Removed dependency on aug_* memory routines (norm)
+ *            Added connection pooling support (norm)
+ *            Standardized API routines to pg_* names (norm)
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "../../db/db_id.h"
+#include "../../db/db_res.h"
+#include "../../db/db_con.h"
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "db_res.h"
+#include "db_val.h"
+#include "pg_con.h"
+#include "pg_type.h"
+
+
+
+/**
+ * Fill the result structure with data from the query
+ */
+int db_postgres_convert_result(const db_con_t* _con, db_res_t* _res)
+{
+	if (!_con || !_res)  {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	if (db_postgres_get_columns(_con, _res) < 0) {
+		LM_ERR("failed to get column names\n");
+		return -2;
+	}
+
+	if (db_postgres_convert_rows(_con, _res, 0, PQntuples(CON_RESULT(_con))) < 0) {
+		LM_ERR("failed to convert rows\n");
+		db_free_columns(_res);
+		return -3;
+	}
+
+        return 0;
+}
+
+/**
+ * Get and convert columns from a result set
+ */
+int db_postgres_get_columns(const db_con_t* _con, db_res_t* _res)
+{
+	int col, datatype;
+
+	if (!_con || !_res)  {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	/* Get the number of rows (tuples) in the query result. */
+	RES_NUM_ROWS(_res) = PQntuples(CON_RESULT(_con));
+
+	/* Get the number of columns (fields) in each row of the query result. */
+	RES_COL_N(_res) = PQnfields(CON_RESULT(_con));
+
+	if (!RES_COL_N(_res)) {
+		LM_DBG("no columns returned from the query\n");
+		return -2;
+	} else {
+		LM_DBG("%d columns returned from the query\n", RES_COL_N(_res));
+	}
+
+	if (db_allocate_columns(_res, RES_COL_N(_res)) != 0) {
+		LM_ERR("could not allocate columns");
+		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(_res); col++) {
+
+		RES_NAMES(_res)[col] = (str*)pkg_malloc(sizeof(str));
+		if (! RES_NAMES(_res)[col]) {
+			LM_ERR("no private memory left\n");
+			db_free_columns(_res);
+			return -4;
+		}
+		LM_DBG("allocate %d bytes for RES_NAMES[%d] at %p", sizeof(str), col,
+				RES_NAMES(_res)[col]);
+
+		/* The pointer that is here returned is part of the result structure. */
+		RES_NAMES(_res)[col]->s = PQfname(CON_RESULT(_con), col);
+		RES_NAMES(_res)[col]->len = strlen(PQfname(CON_RESULT(_con), col));
+
+		LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_res)[col], col,
+				RES_NAMES(_res)[col]->len, RES_NAMES(_res)[col]->s);
+
+		/* get the datatype of the column */
+		switch(datatype = PQftype(CON_RESULT(_con),col))
+		{
+			case INT2OID:
+			case INT4OID:
+			case INT8OID:
+				RES_TYPES(_res)[col] = DB_INT;
+			break;
+
+			case FLOAT4OID:
+			case FLOAT8OID:
+			case NUMERICOID:
+				RES_TYPES(_res)[col] = DB_DOUBLE;
+			break;
+
+			case DATEOID:
+			case TIMESTAMPOID:
+			case TIMESTAMPTZOID:
+				RES_TYPES(_res)[col] = DB_DATETIME;
+			break;
+
+			case BOOLOID:
+			case CHAROID:
+			case VARCHAROID:
+			case BPCHAROID:
+			case TEXTOID:
+				RES_TYPES(_res)[col] = DB_STRING;
+			break;
+
+			case BYTEAOID:
+				RES_TYPES(_res)[col] = DB_BLOB;
+			break;
+
+			case BITOID:
+			case VARBITOID:
+				RES_TYPES(_res)[col] = DB_BITMAP;
+			break;
+				
+			default:
+				LM_WARN("unhandled data type column (%.*s) type id (%d), "
+						"use STRING as default\n", RES_NAMES(_res)[col]->len,
+						RES_NAMES(_res)[col]->s, datatype);
+				RES_TYPES(_res)[col] = DB_STRING;
+			break;
+		}
+	}
+	return 0;
+}
+
+/**
+ * Convert rows from PostgreSQL to db API representation
+ */
+int db_postgres_convert_rows(const db_con_t* _con, db_res_t* _res, int row_start,
+		int row_count)
+{
+	int row, cols, col;
+	char **row_buf, *s;
+	int len, fetch_count;
+
+	if (!_con || !_res)  {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	if (row_count == 0) {
+		LM_ERR("no rows requested from the query\n");
+		return 0;
+	}
+
+	if (!RES_NUM_ROWS(_res)) {
+		LM_DBG("no rows returned from the query\n");
+		return 0;
+	}
+
+	if (row_start < 0)  {
+		LM_ERR("starting row (%d) cannot be less "
+			"then zero, setting it to zero\n", row_start);
+		row_start = 0;
+	}
+
+	if ((row_start + row_count) > RES_NUM_ROWS(_res))  {
+		LM_ERR("starting row + row count cannot be > "
+			"total rows. Setting row count to read remainder of result set\n");
+		row_count = RES_NUM_ROWS(_res) - row_start;
+	}
+
+	/* Save the number of rows in the current fetch */
+	RES_ROW_N(_res) = row_count;
+
+	/* Save the number of columns in the result query */
+	cols = RES_COL_N(_res);
+
+	/*
+	 * Allocate an array of pointers one per column. It that will be used to hold
+	 * the address of the string representation of each column.
+	 */
+	len = sizeof(char *) * cols;
+	row_buf = (char **)pkg_malloc(len);
+	LM_DBG("allocate for %d columns %d bytes in row buffer at %p\n", cols, len, row_buf);
+	if (!row_buf) {
+		LM_ERR("no private memory left\n");
+		return -1;
+	}
+	memset(row_buf, 0, len);
+
+	/* Allocate a row structure for each row in the current fetch. */
+	len = sizeof(db_row_t) * row_count;
+	RES_ROWS(_res) = (db_row_t*)pkg_malloc(len);
+	LM_DBG("allocate %d bytes for %d rows at %p\n", len, row_count, RES_ROWS(_res));
+
+	if (!RES_ROWS(_res)) {
+		LM_ERR("no private memory left\n");
+		return -1;
+	}
+	memset(RES_ROWS(_res), 0, len);
+
+	fetch_count = 0;
+	for(row = row_start; row < (row_start + row_count); row++) {
+		for(col = 0; col < cols; 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(_con), row, col);
+				LM_DBG("PQgetvalue(%p,%d,%d)=[%s]\n", _con, row, col, s);
+				len = strlen(s);
+				row_buf[col] = pkg_malloc(len+1);
+				if (!row_buf[col]) {
+					LM_ERR("no private memory left");
+					return -1;
+				}
+				memset(row_buf[col], 0, len+1);
+				LM_DBG("allocated %d bytes for row_buf[%d] at %p\n", len, col, row_buf[col]);
+
+				strncpy(row_buf[col], s, len);
+				LM_DBG("[%d][%d] Column[%.*s]=[%s]\n",
+					row, col, RES_NAMES(_res)[col]->len, RES_NAMES(_res)[col]->s, row_buf[col]);
+		}
+
+		/*
+		** ASSERT: row_buf contains an entire row in strings
+		*/
+		if(db_postgres_convert_row(_con,_res,&(RES_ROWS(_res)[fetch_count]),row_buf)<0){
+			LM_ERR("failed to convert row #%d\n",  row);
+			RES_ROW_N(_res) = row - row_start;
+			for (col=0; col<cols; col++) {
+				LM_DBG("freeing row_buf[%d] at %p\n", col, row_buf[col]);
+				pkg_free(row_buf[col]);
+			}
+			LM_DBG("freeing row buffer at %p\n", row_buf);
+			pkg_free(row_buf);
+			return -4;
+		}
+		/*
+		 * pkg_free() must be done for the above allocations now that the row
+		 * has been converted. During pg_convert_row (and subsequent pg_str2val)
+		 * processing, data types that don't need to be converted (namely STRINGS
+		 * and STR) have their addresses saved. These data types should not have
+		 * their pkg_malloc() allocations freed here because they are still
+		 * needed.  However, some data types (ex: INT, DOUBLE) should have their
+		 * pkg_malloc() allocations freed because during the conversion process,
+		 * their converted values are saved in the union portion of the db_val_t
+		 * structure. BLOB will be copied during PQunescape in str2val, thus it
+		 * has to be freed here AND in pg_free_row().
+		 *
+		 * Warning: when the converted row is no longer needed, the data types
+		 * whose addresses were saved in the db_val_t structure must be freed
+		 * or a memory leak will happen. This processing should happen in the
+		 * pg_free_row() subroutine. The caller of this routine should ensure
+		 * that pg_free_rows(), pg_free_row() or pg_free_result() is eventually
+		 * called.
+		 */
+		for (col=0; col<cols; col++) {
+			switch (RES_TYPES(_res)[col]) {
+				case DB_STRING:
+				case DB_STR:
+					break;
+				default:
+					LM_DBG("[%d][%d] Col[%.*s] Type[%d] "
+						"Freeing row_buf[%p]\n", row, col,
+						RES_NAMES(_res)[col]->len, RES_NAMES(_res)[col]->s,
+						RES_TYPES(_res)[col], row_buf[col]);
+					LM_DBG("freeing row_buf[%d] at %p\n", col, row_buf[col]);
+					pkg_free(row_buf[col]);
+			}
+			/*
+			 * The following housekeeping may not be technically required, but it
+			 * is a good practice to NULL pointer fields that are no longer valid.
+			 * Note that DB_STRING fields have not been pkg_free(). NULLing DB_STRING
+			 * fields would normally not be good to do because a memory leak would
+			 * occur.  However, the pg_convert_row() routine  has saved the DB_STRING
+			 * pointer in the db_val_t structure.  The db_val_t structure will 
+			 * eventually be used to pkg_free() the DB_STRING storage.
+			 */
+			row_buf[col] = (char *)NULL;
+		}
+	fetch_count++;
+	}
+
+	LM_DBG("freeing row buffer at %p\n", row_buf);
+	pkg_free(row_buf);
+	row_buf = NULL;
+	return 0;
+}
+
+
+/**
+ * Convert a row from the result query into db API representation
+ */
+int db_postgres_convert_row(const db_con_t* _con, db_res_t* _res, db_row_t* _row,
+		char **row_buf)
+{
+	int col, len;
+
+	if (!_con || !_res || !_row)  {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	/*
+	 * Allocate storage to hold the data type value converted from a string
+	 * because PostgreSQL returns (most) data as strings
+	 */
+	len = sizeof(db_val_t) * RES_COL_N(_res);
+	ROW_VALUES(_row) = (db_val_t*)pkg_malloc(len);
+
+	if (!ROW_VALUES(_row)) {
+		LM_ERR("no private memory left\n");
+		return -1;
+	}
+	LM_DBG("allocate %d bytes for row values at %p\n", sizeof(db_val_t) * RES_COL_N(_res),
+		ROW_VALUES(_row));
+	ROW_N(_row) = RES_COL_N(_res);
+	memset(ROW_VALUES(_row), 0, len);
+
+	/* Save the number of columns in the ROW structure */
+	ROW_N(_row) = RES_COL_N(_res);
+
+	/* For each column in the row */
+	for(col = 0; col < ROW_N(_row); col++) {
+		/* Convert the string representation into the value representation */
+		if (db_postgres_str2val(RES_TYPES(_res)[col], &(ROW_VALUES(_row)[col]),
+		row_buf[col], strlen(row_buf[col])) < 0) {
+			LM_ERR("failed to convert value\n");
+			LM_DBG("free row at %pn", _row);
+			db_free_row(_row);
+			return -3;
+		}
+	}
+	return 0;
+}

+ 39 - 0
modules/db_postgres/km_db_res.h

@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 DB_PG_RES_H
+#define DB_PG_RES_H
+
+#include "../../db/db_row.h"
+
+int db_postgres_convert_result(const db_con_t* _h, db_res_t* _r);
+
+int db_postgres_convert_row(const db_con_t* _h, db_res_t* _res, db_row_t* _r,
+	char **row_buf);
+
+int db_postgres_get_columns(const db_con_t* _h, db_res_t* _r);
+
+int db_postgres_convert_rows(const db_con_t* _h, db_res_t* _r, int row_start, int row_count);
+
+#endif

+ 293 - 0
modules/db_postgres/km_db_val.c

@@ -0,0 +1,293 @@
+/*
+ * $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 openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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)
+ *
+ */
+
+
+#include "../../db/db_val.h"
+#include "../../db/db_ut.h"
+#include "../../dprint.h"
+#include "pg_con.h"
+
+#include "../../mem/mem.h"
+#include "db_val.h"
+
+#include <string.h>
+#include <time.h>
+
+
+/*
+ * Convert a str to a db value, does not 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
+ */
+int db_postgres_str2val(const db_type_t _t, db_val_t* _v, const char* _s, const int _l)
+{
+	static str dummy_string = {"", 0};
+
+	if (!_v) {
+		LM_ERR("invalid parameter value\n");
+	}
+
+	if (!_s) {
+		memset(_v, 0, sizeof(db_val_t));
+		/* Initialize the string pointers to a dummy empty
+		 * string so that we do not crash when the NULL flag
+		 * is set but the module does not check it properly
+		 */
+		VAL_STRING(_v) = dummy_string.s;
+		VAL_STR(_v) = dummy_string;
+		VAL_BLOB(_v) = dummy_string;
+		VAL_TYPE(_v) = _t;
+		VAL_NULL(_v) = 1;
+		return 0;
+	}
+	VAL_NULL(_v) = 0;
+
+	switch(_t) {
+	case DB_INT:
+		LM_DBG("converting INT [%s]\n", _s);
+		if (db_str2int(_s, &VAL_INT(_v)) < 0) {
+			LM_ERR("failed to convert INT value from string\n");
+			return -2;
+		} else {
+			VAL_TYPE(_v) = DB_INT;
+			return 0;
+		}
+		break;
+
+	case DB_BITMAP:
+		LM_DBG("converting BITMAP [%s]\n", _s);
+		if (db_str2int(_s, &VAL_INT(_v)) < 0) {
+			LM_ERR("failed to convert BITMAP value from string\n");
+			return -3;
+		} else {
+			VAL_TYPE(_v) = DB_BITMAP;
+			return 0;
+		}
+		break;
+	
+	case DB_DOUBLE:
+		LM_DBG("converting DOUBLE [%s]\n", _s);
+		if (db_str2double(_s, &VAL_DOUBLE(_v)) < 0) {
+			LM_ERR("failed to convert DOUBLE value from string\n");
+			return -4;
+		} else {
+			VAL_TYPE(_v) = DB_DOUBLE;
+			return 0;
+		}
+		break;
+
+	case DB_STRING:
+		LM_DBG("converting STRING [%s]\n", _s);
+		VAL_STRING(_v) = _s;
+		VAL_TYPE(_v) = DB_STRING;
+		VAL_FREE(_v) = 1;
+		return 0;
+
+	case DB_STR:
+		LM_DBG("converting STR [%.*s]\n", _l, _s);
+		VAL_STR(_v).s = (char*)_s;
+		VAL_STR(_v).len = _l;
+		VAL_TYPE(_v) = DB_STR;
+		VAL_FREE(_v) = 1;
+		_s = 0;
+		return 0;
+
+	case DB_DATETIME:
+		LM_DBG("converting DATETIME [%s]\n", _s);
+		if (db_str2time(_s, &VAL_TIME(_v)) < 0) {
+			LM_ERR("failed to convert datetime\n");
+			return -5;
+		} else {
+			VAL_TYPE(_v) = DB_DATETIME;
+			return 0;
+		}
+		break;
+
+	case DB_BLOB:
+		LM_DBG("converting BLOB [%.*s]\n", _l, _s);
+		/* PQunescapeBytea:  Converts a string representation of binary data
+		 * into binary data - the reverse of PQescapeBytea.
+		 * This is needed when retrieving bytea data in text format,
+		 * but not when retrieving it in binary format.
+		 */
+		VAL_BLOB(_v).s = (char*)PQunescapeBytea((unsigned char*)_s, 
+			(size_t*)(void*)&(VAL_BLOB(_v).len) );
+		VAL_TYPE(_v) = DB_BLOB;
+		VAL_FREE(_v) = 1;
+		LM_DBG("got blob len %d\n", _l);
+		return 0;
+	}
+	return -6;
+}
+
+
+/*
+ * Used when converting result from a query
+ */
+int db_postgres_val2str(const db_con_t* _con, const db_val_t* _v, char* _s, int* _len)
+{
+	int l, ret;
+	int pgret;
+	char *tmp_s;
+	size_t tmp_len;
+	char* old_s;
+
+	if ((!_v) || (!_s) || (!_len) || (!*_len)) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	if (VAL_NULL(_v)) {
+		*_len = snprintf(_s, *_len, "NULL");
+		return 0;
+	}
+	
+	switch(VAL_TYPE(_v)) {
+	case DB_INT:
+		if (db_int2str(VAL_INT(_v), _s, _len) < 0) {
+			LM_ERR("failed to convert string to int\n");
+			return -2;
+		} else {
+			return 0;
+		}
+		break;
+
+	case DB_BITMAP:
+		if (db_int2str(VAL_BITMAP(_v), _s, _len) < 0) {
+			LM_ERR("failed to convert string to int\n");
+			return -3;
+		} else {
+			return 0;
+		}
+		break;
+
+	case DB_DOUBLE:
+		if (db_double2str(VAL_DOUBLE(_v), _s, _len) < 0) {
+			LM_ERR("failed to convert string to double\n");
+			return -3;
+		} else {
+			return 0;
+		}
+		break;
+
+	case DB_STRING:
+		l = strlen(VAL_STRING(_v));
+		if (*_len < (l * 2 + 3)) {
+			LM_ERR("destination buffer too short for string\n");
+			return -4;
+		} 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 -4;
+			}
+			LM_DBG("PQescapeStringConn: in: %d chars,"
+				" out: %d chars\n", l, ret);
+			_s += ret;
+			*_s++ = '\'';
+			*_s = '\0'; /* FIXME */
+			*_len = _s - old_s;
+			return 0;
+		}
+		break;
+
+	case DB_STR:
+		l = VAL_STR(_v).len;
+		if (*_len < (l * 2 + 3)) {
+			LM_ERR("destination buffer too short for str\n");
+			return -5;
+		} 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 -5;
+			}
+	        LM_DBG("PQescapeStringConn: in: %d chars, out: %d chars\n", l, ret);
+			_s += ret;
+			*_s++ = '\'';
+			*_s = '\0'; /* FIXME */
+			*_len = _s - old_s;
+			return 0;
+		}
+		break;
+
+	case DB_DATETIME:
+		if (db_time2str(VAL_TIME(_v), _s, _len) < 0) {
+			LM_ERR("failed to convert string to time_t\n");
+			return -6;
+		} else {
+			return 0;
+		}
+		break;
+
+	case DB_BLOB:
+		l = VAL_BLOB(_v).len;
+		if (*_len < (l * 2 + 3)) {
+			LM_ERR("destination buffer too short for blob\n");
+			return -7;
+		} 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("PQescapeBytea failed\n");
+				return -7;
+			}
+			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 -7;
+	}
+	return -8;
+}

+ 32 - 0
modules/db_postgres/km_db_val.h

@@ -0,0 +1,32 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 DB_PG_VAL_H
+#define DB_PG_VAL_H
+
+int db_postgres_str2val(const db_type_t _t, db_val_t* _v, const char* _s, const int _l);
+
+int db_postgres_val2str(const db_con_t* _con, const db_val_t* _v, char* _s, int* _len);
+
+#endif

+ 527 - 0
modules/db_postgres/km_dbase.c

@@ -0,0 +1,527 @@
+/*
+ * $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) 2006 Norman Brandinger
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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, OpenSER 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)
+ */
+
+#define MAXCOLUMNS	512
+
+#include <string.h>
+#include <stdio.h>
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "../../db/db.h"
+#include "../../db/db_ut.h"
+#include "../../db/db_query.h"
+#include "dbase.h"
+#include "pg_con.h"
+#include "db_val.h"
+#include "db_res.h"
+
+static int free_query(const db_con_t* _con);
+
+
+/*
+** pg_init	initialize database for future queries
+**
+**	Arguments :
+**		char *_url;	sql database to open
+**
+**	Returns :
+**		db_con_t * NULL upon error
+**		db_con_t * if successful
+**
+**	Notes :
+**		pg_init must be called prior to any database functions.
+*/
+
+db_con_t *db_postgres_init(const str* _url)
+{
+	return db_do_init(_url, (void*) db_postgres_new_connection);
+}
+
+
+/*
+** pg_close	last function to call when db is no longer needed
+**
+**	Arguments :
+**		db_con_t * the connection to shut down, as supplied by pg_init()
+**
+**	Returns :
+**		(void)
+**
+**	Notes :
+**		All memory and resources are freed.
+*/
+
+void db_postgres_close(db_con_t* _h)
+{
+	db_do_close(_h, db_postgres_free_connection);
+}
+
+/*
+** submit_query	run a query
+**
+**	Arguments :
+**		db_con_t *	as previously supplied by pg_init()
+**		char *_s	the text query to run
+**
+**	Returns :
+**		0 upon success
+**		negative number upon failure
+*/
+
+static int db_postgres_submit_query(const db_con_t* _con, const str* _s)
+{
+	if(! _con || !_s || !_s->s)
+	{
+		LM_ERR("invalid parameter value\n");
+		return(-1);
+	}
+
+	/* this bit of nonsense in case our connection get screwed up */
+	switch(PQstatus(CON_CONNECTION(_con)))
+	{
+		case CONNECTION_OK: 
+			break;
+		case CONNECTION_BAD:
+			LM_DBG("connection reset\n");
+			PQreset(CON_CONNECTION(_con));
+			break;
+		case CONNECTION_STARTED:
+		case CONNECTION_MADE:
+		case CONNECTION_AWAITING_RESPONSE:
+		case CONNECTION_AUTH_OK:
+		case CONNECTION_SETENV:
+		case CONNECTION_SSL_STARTUP:
+		case CONNECTION_NEEDED:
+		default:
+			LM_ERR("%p PQstatus(%s) invalid: %.*s\n", _con,
+				PQerrorMessage(CON_CONNECTION(_con)), _s->len, _s->s);
+			return -1;
+	}
+
+	/* free any previous query that is laying about */
+	if(CON_RESULT(_con))
+	{
+		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;
+}
+
+/*
+ *
+ * pg_fetch_result: Gets a partial result set.
+ *
+ */
+int db_postgres_fetch_result(const db_con_t* _con, db_res_t** _res, const int nrows)
+{
+	int rows;
+	PGresult *res = NULL;
+	ExecStatusType pqresult;
+
+	if (!_con || !_res || nrows < 0) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	/* exit if the fetch count is zero */
+	if (nrows == 0) {
+		if (*_res)
+			db_free_result(*_res);
+
+        *_res = 0;
+		return 0;
+	}
+
+	if (*_res == NULL) {
+		/* Allocate a new result structure */
+		*_res = db_new_result();
+
+		/* Get the result of the previous query */
+		while (1) {
+			if ((res = PQgetResult(CON_CONNECTION(_con)))) {
+				CON_RESULT(_con) = res;
+			} else {
+				break;
+			}
+		}
+		pqresult = PQresultStatus(CON_RESULT(_con));
+		LM_DBG("%p PQresultStatus(%s) PQgetResult(%p)\n", _con,
+			PQresStatus(pqresult), CON_RESULT(_con));
+
+		switch(pqresult) {
+			case PGRES_COMMAND_OK:
+				/* Successful completion of a command returning no data (such as INSERT or UPDATE). */
+				return 0;
+			case PGRES_TUPLES_OK:
+				/* Successful completion of a command returning data (such as a SELECT or SHOW). */
+				if (db_postgres_get_columns(_con, *_res) < 0) {
+					LM_ERR("failed to get column names\n");
+					return -2;
+				}
+				break;
+			case PGRES_EMPTY_QUERY:
+			case PGRES_COPY_OUT:
+			case PGRES_COPY_IN:
+			case PGRES_BAD_RESPONSE:
+			case PGRES_NONFATAL_ERROR:
+			case PGRES_FATAL_ERROR:
+				LM_WARN("%p - probable invalid query\n", _con);
+			default:
+				LM_WARN("%p - PQresultStatus(%s)\n",
+					_con, PQresStatus(pqresult));
+				if (*_res) 
+					db_free_result(*_res);
+        		*_res = 0;
+				return 0;
+		}
+
+	} else {
+		if(RES_ROWS(*_res) != NULL) {
+			db_free_rows(*_res);
+		}
+		RES_ROW_N(*_res) = 0;
+	}
+
+	/* 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, RES_LAST_ROW(*_res), RES_ROW_N(*_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;
+}
+
+/*
+** free_query	clear the db channel and clear any old query result status
+**
+**	Arguments :
+**		db_con_t *	as previously supplied by pg_init()
+**
+**	Returns :
+**		0 upon success
+**		negative number upon failure
+*/
+
+static int free_query(const db_con_t* _con)
+{
+	if(CON_RESULT(_con))
+	{
+		LM_DBG("PQclear(%p) result set\n", CON_RESULT(_con));
+		PQclear(CON_RESULT(_con));
+		CON_RESULT(_con) = 0;
+	}
+
+	return 0;
+}
+
+/*
+** db_free_query	free the query and free the result memory
+**
+**	Arguments :
+**		db_con_t *	as previously supplied by pg_init()
+**		db_res_t *	the result of a query
+**
+**	Returns :
+**		0 upon success
+**		negative number upon failure
+*/
+
+int db_postgres_free_query(db_con_t* _con, db_res_t* _r)
+{
+	free_query(_con);
+	if (_r) db_free_result(_r);
+	_r = 0;
+
+	return 0;
+}
+
+/*
+ * Query table for specified rows
+ * _con: structure representing database connection
+ * _k: key names
+ * _op: operators
+ * _v: values of the keys that must match
+ * _c: column names to return
+ * _n: nmber of key=values pairs to compare
+ * _nc: number of columns to return
+ * _o: order by the specified column
+ */
+int db_postgres_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op,
+	     const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,
+	     const db_key_t _o, db_res_t** _r)
+{
+	return db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, _r, db_postgres_val2str,
+		db_postgres_submit_query, db_postgres_store_result);
+}
+
+
+/*
+ * Execute a raw SQL query
+ */
+int db_postgres_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r)
+{
+	return db_do_raw_query(_h, _s, _r, db_postgres_submit_query,
+		db_postgres_store_result);
+}
+
+/*
+ * Retrieve result set
+ *
+ * Input:
+ *   db_con_t*  _con Structure representing the database connection
+ *   db_res_t** _r pointer to a structure represending the result set
+ *
+ * Output:
+ *   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.
+ *
+ *   return < 0: If the status of the last command was not handled or if the
+ *   convert_result() returned an error.
+ *
+ * Notes:
+ *   A new result structure is allocated on every call to this routine.
+ *
+ *   If this routine returns 0, it is the callers responsbility to free the
+ *   result structure. If this routine returns < 0, then the result structure
+ *   is freed before returning to the caller.
+ *
+ */
+
+int db_postgres_store_result(const db_con_t* _con, db_res_t** _r)
+{
+	PGresult *res = NULL;
+	ExecStatusType pqresult;
+	int rc = 0;
+
+	*_r = db_new_result();
+
+	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("%p Error returned from convert_result()\n", _con);
+				if (*_r) db_free_result(*_r);
+
+        		*_r = 0;
+				rc = -4;
+			}
+			rc =  0;
+			break;
+		case PGRES_EMPTY_QUERY:
+		case PGRES_COPY_OUT:
+		case PGRES_COPY_IN:
+		case PGRES_BAD_RESPONSE:
+		case PGRES_NONFATAL_ERROR:
+		case PGRES_FATAL_ERROR:
+			LM_WARN("%p Probable invalid query\n", _con);
+		default:
+			LM_WARN("%p: %s\n", _con, PQresStatus(pqresult));
+        	LM_WARN("%p: %s\n", _con, PQresultErrorMessage(CON_RESULT(_con)));
+			if (*_r) db_free_result(*_r);
+
+			*_r = 0;
+			rc = (int)pqresult;
+			break;
+	}
+
+	free_query(_con);
+	return (rc);
+}
+
+/*
+ * Insert a row into specified table
+ * _con: structure representing database connection
+ * _k: key names
+ * _v: values of the keys
+ * _n: number of key=value pairs
+ */
+int db_postgres_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v,
+		const int _n)
+{
+	db_res_t* _r = NULL;
+
+	int tmp = db_do_insert(_h, _k, _v, _n, db_postgres_val2str, db_postgres_submit_query);
+	// finish the async query, otherwise the next query will not complete
+	if (db_postgres_store_result(_h, &_r) != 0)
+		LM_WARN("unexpected result returned");
+	
+	if (_r)
+		db_free_result(_r);
+
+	return tmp;
+}
+
+
+/*
+ * Delete a row from the specified table
+ * _con: structure representing database connection
+ * _k: key names
+ * _o: operators
+ * _v: values of the keys that must match
+ * _n: number of key=value pairs
+ */
+int db_postgres_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+		const db_val_t* _v, const int _n)
+{
+	db_res_t* _r = NULL;
+	int tmp = db_do_delete(_h, _k, _o, _v, _n, db_postgres_val2str,
+		db_postgres_submit_query);
+
+	if (db_postgres_store_result(_h, &_r) != 0)
+		LM_WARN("unexpected result returned");
+	
+	if (_r)
+		db_free_result(_r);
+
+	return tmp;
+}
+
+
+/*
+ * Update some rows in the specified table
+ * _con: structure representing database connection
+ * _k: key names
+ * _o: operators
+ * _v: values of the keys that must match
+ * _uk: updated columns
+ * _uv: updated values of the columns
+ * _n: number of key=value pairs
+ * _un: number of columns to update
+ */
+int db_postgres_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+		const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n,
+		const int _un)
+{
+	db_res_t* _r = NULL;
+	int tmp = db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_postgres_val2str,
+		db_postgres_submit_query);
+
+	if (db_postgres_store_result(_h, &_r) != 0)
+		LM_WARN("unexpected result returned");
+	
+	if (_r)
+		db_free_result(_r);
+
+	return tmp;
+}
+
+
+/*
+ * Store name of table that will be used by
+ * subsequent database functions
+ */
+int db_postgres_use_table(db_con_t* _con, const str* _t)
+{
+	return db_use_table(_con, _t);
+}

+ 114 - 0
modules/db_postgres/km_dbase.h

@@ -0,0 +1,114 @@
+/*
+ * $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 openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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)
+ *
+ */
+
+
+#ifndef DBASE_H
+#define DBASE_H
+
+#include "../../db/db_con.h"
+#include "../../db/db_res.h"
+#include "../../db/db_key.h"
+#include "../../db/db_op.h"
+#include "../../db/db_val.h"
+
+
+/**
+ * Initialize database connection
+ */
+db_con_t* db_postgres_init(const str* _url);
+
+/**
+ * Close a database connection
+ */
+void db_postgres_close(db_con_t* _h);
+
+/**
+ * Return result of previous query
+ */
+int db_postgres_store_result(const db_con_t* _h, db_res_t** _r);
+
+
+/**
+ * Free all memory allocated by get_result
+ */
+int db_postgres_free_query(db_con_t* _h, db_res_t* _r);
+
+
+/**
+ * Do a query
+ */
+int db_postgres_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op,
+		const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,
+		const db_key_t _o, db_res_t** _r);
+
+/**
+ * Raw SQL query
+ */
+int db_postgres_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r);
+
+
+/**
+ * Insert a row into table
+ */
+int db_postgres_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v,
+		const int _n);
+
+
+/**
+ * Delete a row from table
+ */
+int db_postgres_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+		const db_val_t* _v, const int _n);
+
+
+/**
+ * Update a row in table
+ */
+int db_postgres_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
+		const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n,
+		const int _un);
+
+/**
+ * fetch rows from a result
+ */
+int db_postgres_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows);
+
+
+/**
+ * Store name of table that will be used by
+ * subsequent database functions
+ */
+int db_postgres_use_table(db_con_t* _h, const str* _t);
+
+
+#endif /* DBASE_H */

+ 52 - 0
modules/db_postgres/km_doc/postgres.sgml

@@ -0,0 +1,52 @@
+<!DOCTYPE Book PUBLIC "-//OASIS//DTD DocBook V4.2//EN" [
+
+
+<!ENTITY user SYSTEM "postgres_user.sgml">
+<!ENTITY devel SYSTEM "postgres_devel.sgml">
+<!ENTITY faq SYSTEM "postgres_faq.sgml">
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../doc/entities.sgml">
+%docentities;
+
+]>
+
+<book>
+    <bookinfo>
+	<title>postgres Module</title>
+	<productname class="trade">&sername;</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>
+    
+    &user;
+    &devel;
+    &faq;
+    
+</book>

+ 22 - 0
modules/db_postgres/km_doc/postgres_devel.sgml

@@ -0,0 +1,22 @@
+<!-- Module Developer's Guide -->
+
+<chapter>
+    <chapterinfo>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </chapterinfo>
+    <title>Developer's Guide</title>
+    <para>
+	The module does not provide any <acronym>API</acronym> to use in other &ser; modules.
+    </para>
+</chapter>
+
+<!-- Keep this element at the end of the file
+Local Variables:
+sgml-parent-document: ("postgres.sgml" "book" "chapter")
+End:
+-->

+ 70 - 0
modules/db_postgres/km_doc/postgres_faq.sgml

@@ -0,0 +1,70 @@
+<!-- Module FAQ -->
+
+<chapter>
+    <chapterinfo>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </chapterinfo>
+    <title>Frequently Asked Questions</title>
+    <qandaset defaultlabel="number">
+	<qandaentry>
+	    <question>
+		<para>Where can I find more about OpenSER?</para>
+	    </question>
+	    <answer>
+		<para>
+			Take a look at &serhomelink;.
+		</para>
+	    </answer>
+	</qandaentry>
+	<qandaentry>
+	    <question>
+		<para>Where can I post a question about this module?</para>
+	    </question>
+	    <answer>
+		<para>
+			First at all check if your question was already answered on one of
+			our mailing lists: 
+		</para>
+		<itemizedlist>
+		    <listitem>
+			<para>User Mailing List - &seruserslink;</para>
+		    </listitem>
+		    <listitem>
+			<para>Developer Mailing List - &serdevlink;</para>
+		    </listitem>
+		</itemizedlist>
+		<para>
+			E-mails regarding any stable &ser; release should be sent to 
+			&serusersmail; and e-mails regarding development versions
+			should be sent to &serdevmail;.
+		</para>
+		<para>
+			If you want to keep the mail private, send it to 
+			&serhelpmail;.
+		</para>
+	    </answer>
+	</qandaentry>
+	<qandaentry>
+	    <question>
+		<para>How can I report a bug?</para>
+	    </question>
+	    <answer>
+		<para>
+			Please follow the guidelines provided at:
+			&serbugslink;.
+		</para>
+	    </answer>
+	</qandaentry>
+    </qandaset>
+</chapter>
+
+<!-- Keep this element at the end of the file
+Local Variables:
+sgml-parent-document: ("postgres.sgml" "Book" "chapter")
+End:
+-->

+ 72 - 0
modules/db_postgres/km_doc/postgres_user.sgml

@@ -0,0 +1,72 @@
+<!-- Module User's Guide -->
+
+<chapter>
+	<chapterinfo>
+	<revhistory>
+		<revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+		</revision>
+	</revhistory>
+	</chapterinfo>
+	<title>User's Guide</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>Module description</para>
+	</section>
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&ser; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>No dependencies on other &ser; 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
+		&ser; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</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 & Running</title>
+	<para>Notes about installation and running.</para>
+	</section>
+</chapter>
+
+<!-- Keep this element at the end of the file
+Local Variables:
+sgml-parent-document: ("postgres.sgml" "Book" "chapter")
+End:
+-->

+ 118 - 0
modules/db_postgres/km_pg_con.c

@@ -0,0 +1,118 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2001-2004 iptel.org
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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
+ */
+
+#include "pg_con.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include <string.h>
+#include <time.h>
+
+
+/*
+ * 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)
+{
+	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;
+}
+
+
+/*
+ * Close the connection and release memory
+ */
+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);
+}

+ 79 - 0
modules/db_postgres/km_pg_con.h

@@ -0,0 +1,79 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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)
+ *
+ */
+
+
+#ifndef PG_CON_H
+#define PG_CON_H
+
+#include "../../db/db_pool.h"
+#include "../../db/db_id.h"
+
+#include <time.h>
+#include <libpq-fe.h>
+
+/*
+ * Postgres specific connection data
+ */
+struct pg_con {
+	struct db_id* id;        /* Connection identifier */
+	unsigned int ref;        /* Reference count */
+	struct pool_con* next;   /* Next connection in the pool */
+
+	int connected;
+	char *sqlurl;		/* the url we are connected to, all connection memory parents from this */
+	PGconn *con;		/* this is the postgres connection */
+	PGresult *res;		/* this is the current result */
+	char**  row;		/* Actual row in the result */
+	time_t timestamp;	/* Timestamp of last query */
+
+};
+
+#define CON_SQLURL(db_con)     (((struct pg_con*)((db_con)->tail))->sqlurl)
+#define CON_RESULT(db_con)     (((struct pg_con*)((db_con)->tail))->res)
+#define CON_CONNECTION(db_con) (((struct pg_con*)((db_con)->tail))->con)
+#define CON_CONNECTED(db_con)  (((struct pg_con*)((db_con)->tail))->connected)
+#define CON_ROW(db_con)	       (((struct pg_con*)((db_con)->tail))->row)
+#define CON_TIMESTAMP(db_con)  (((struct pg_con*)((db_con)->tail))->timestamp)
+#define CON_ID(db_con) 	       (((struct pg_con*)((db_con)->tail))->id)
+
+/*
+ * Create a new connection structure,
+ * open the PostgreSQL connection and set reference count to 1
+ */
+struct pg_con* db_postgres_new_connection(struct db_id* id);
+
+/*
+ * Close the connection and release memory
+ */
+void db_postgres_free_connection(struct pool_con* con);
+
+#endif /* PG_CON_H */

+ 94 - 0
modules/db_postgres/km_pg_type.h

@@ -0,0 +1,94 @@
+/*
+ * $Id$
+ *
+ * POSTGRES module, portions of this code were templated using
+ * the mysql module, thus it's similarity.
+ *
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of openser, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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