Selaa lähdekoodia

Postgres module update, includes:

- Support for connection pools
- Support for multiple simultaneous results
- No static buffer usage, uses more efficient PQExecParams
- Support for database reconnects (not handled by libpq)
- Support for binary parameters in PQExecParams
  (this brings us one step closer to prepared statements
   and removes tedious and error prone text conversions)
- No data is copied from postgres result unless necessary
- SQL injection vulnerability fixed
- Configurable connectionn timeout
- Configurable number of reconnect attempts
- Support for bitmap data type
- Support for binary data values
- Support for timestamp format auto-detection
Jan Janak 20 vuotta sitten
vanhempi
commit
d464060708

+ 0 - 632
modules/db_postgres/aug_alloc.c

@@ -1,632 +0,0 @@
-/*
- * $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 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
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
- *
- */
-/*
-** ________________________________________________________________________
-**
-**
-**                      $RCSfile$
-**                     $Revision$
-**
-**             Last change $Date$
-**           Last change $Author$
-**                        $State$
-**                       $Locker$
-**
-**               Original author: Andrew Fullford
-**
-**           Copyright (C) August Associates  1995
-**
-** ________________________________________________________________________
-*/
-
-#include "aug_std.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-typedef double MemAlign;
-typedef augUInt32 MemMagic;
-typedef union MemHead MemHead;
-typedef struct MemOpt MemOpt;
-typedef struct MemDestructor MemDestructor;	/* not yet implemented */
-
-/*
-**  One of these MemHead structs is allocated at the head of
-**  each alloc, plus an extra magic number at the end area.
-**  This gives an allocation overhead of:
-**
-**	malloc_overhead + sizeof MemHead + sizeof MemMagic
-**
-**  "Notes" entry for the man page: the allocation overhead is way
-**  too high.  (On a 32bit machine and assuming a malloc overhead
-**  of 8 bytes, the total will be 8 + 32 + 4 = 44 bytes).
-*/
-struct MemHeadStruct
-{
-	MemHead *parent, *sibling, *child;
-	MemOpt *options;
-	char *end;
-	char *file;
-	augUInt32 line;
-	MemMagic magic;
-};
-
-/*
-**  Attempt to guarantee alignment.
-*/
-union MemHead
-{
-	struct MemHeadStruct m;
-	MemAlign align[1];
-};
-
-/*
-**  MemOpt holds optional features.  The only current example
-**  is the memory destructor state.
-*/
-struct MemOpt
-{
-	MemMagic magic;
-	MemDestructor *destructor_list;
-};
-
-/*
-**  These magic numbers are used to validate headers, force memory
-**  to a known state, etc.
-*/
-#define MEM_MAGIC_BOUND	0xC0EDBABE
-#define MEM_MAGIC_FILL	0xDEADC0DE
-
-static int mem_bad(MemHead *mem, char *where, char *file, int line)
-{
-	aug_abort(file, line, "Corrupted memory in %s", where);
-	return 0;
-}
-
-/*
-**  Calculate the MemHead address given an aug_alloc() pointer.
-*/
-#define MEM_CROWN(alloc) ((MemHead *)(((char *)alloc) - sizeof (MemHead)))
-#define MEM_DECAPITATE(mem) ((void *)(((char *)mem) + sizeof (MemHead)))
-
-static MemMagic mem_magic = MEM_MAGIC_BOUND;
-#define MEM_TAIL(p) (memcmp((p)->m.end,(char*)&mem_magic,sizeof mem_magic)==0)
-#define MEM_CHECK(p,w) ((p) && \
-			((p)->m.magic != MEM_MAGIC_BOUND || !MEM_TAIL(p)) && \
-			mem_bad((p),(w),file,line))
-
-/*  Initialize stats structure with estimated overhead */
-static augAllocStats mem_stats = {sizeof (MemHead) + sizeof (MemMagic) + 8, 0};
-
-static augNoMemFunc *mem_nomem_func = 0;
-static void mem_nomem(size_t size, char *func, char *file, int line)
-{
-	static augBool active = augFALSE;
-	char *module;
-
-	if(!func)
-		func = "unknown function";
-
-	if(active)
-		fprintf(stderr, "\r\n\nPANIC: nomem bounce\r\n\n");
-	else
-	{
-		active = augTRUE;
-		if(mem_nomem_func)
-			(*mem_nomem_func)(size, func, file, line);
-	}
-
-	fprintf(stderr, "\r\n\n");
-
-	module = aug_module();
-	if(module && *module)
-		fprintf(stderr, "FATAL in %s: ", module);
-	else
-		fprintf(stderr, "FATAL: ");
-
-	fprintf(stderr, "%s failure allocating %lu bytes ", func, size);
-
-	if(file && *file)
-		fprintf(stderr, "from +%d %s \r\n", line, file);
-	else
-		fprintf(stderr, "(unknown location) \r\n");
-
-	fprintf(stderr, "              Current allocations: %10lu \r\n",
-		(mem_stats.alloc_ops - mem_stats.free_ops));
-	fprintf(stderr, "                Total allocations: %10lu \r\n",
-		mem_stats.alloc_ops);
-	fprintf(stderr, "              Total reallocations: %10lu \r\n",
-		mem_stats.realloc_ops);
-	fprintf(stderr, "                      Total frees: %10lu \r\n",
-		mem_stats.free_ops);
-	fprintf(stderr, "Estimated total heap use (KBytes): %10lu \r\n",
-		(mem_stats.current_bytes_allocated +
-		(mem_stats.alloc_ops - mem_stats.free_ops) *
-		mem_stats.estimated_overhead_per_alloc + 512)/1024);
-	fprintf(stderr, "\n");
-		
-	aug_exit(augEXIT_NOMEM);
-}
-
-static void *mem_alloc(size_t size, void *parent, char *file, int line)
-{
-	MemHead *mem, *par;
-	DABNAME("mem_alloc");
-
-	if(parent)
-	{
-		par = MEM_CROWN(parent);
-		MEM_CHECK(par, "parent");
-		MEM_CHECK(par->m.child, "sibling");
-		MEM_CHECK(par->m.sibling, "uncle");
-	}
-	else
-		par = 0;
-
-	mem_stats.current_bytes_allocated += size;
-	mem_stats.alloc_ops++;
-
-	/*  Adjust for overhead  */
-	size += sizeof (MemHead);
-
-	mem = malloc(size + sizeof (MemMagic));
-	if(!mem)
-		mem_nomem(size, "aug_alloc", file, line);
-
-	if(DABLEVEL(DAB_STD))
-	{
-		unsigned long *p;
-		p = (unsigned long *)mem;
-		while((char *)p <= (char *)mem + size)
-			*p++ = MEM_MAGIC_FILL;
-	}
-
-	mem->m.magic = MEM_MAGIC_BOUND;
-	mem->m.file = file;
-	mem->m.line = line;
-	mem->m.end = (char *)mem + size;
-	mem->m.options = 0;
-	mem->m.child = 0;
-	mem->m.parent = par;
-
-	if(par)
-	{
-		if((mem->m.sibling = par->m.child))
-			mem->m.sibling->m.parent = mem;
-		par->m.child = mem;
-	}
-	else
-		mem->m.sibling = 0;
-
-	memcpy(mem->m.end, (char *)&mem_magic, sizeof mem_magic);
-
-	return MEM_DECAPITATE(mem);
-}
-
-static void mem_free(MemHead *mem)
-{
-	size_t size;
-	DABNAME("mem_free");
-
-	while(mem)
-	{
-		MemHead *next = mem->m.sibling;
-		if(mem->m.child)
-			mem_free(mem->m.child);
-		size = (char *)mem->m.end - (char *)mem;
-		size -= sizeof (MemHead) + sizeof (MemMagic);
-		mem_stats.current_bytes_allocated -= size;
-		mem_stats.free_ops++;
-		if(DABLEVEL(DAB_STD))
-		{
-			unsigned long *p = (unsigned long *)(mem+1);
-
-			while((char *)p <= mem->m.end)
-				*p++ = MEM_MAGIC_FILL;
-			p = (unsigned long *)mem;
-			while(p < (unsigned long *)(mem+1))
-				*p++ = MEM_MAGIC_FILL;
-		}
-		free(mem);
-		mem = next;
-	}
-}
-
-static augBool mem_find(MemHead *mem, MemHead *p)
-{
-	while(mem)
-	{
-		MemHead *next;
-
-		if(mem == p)
-			return augTRUE;
-		next = mem->m.sibling;
-		if(mem->m.child)
-			if(mem_find(mem->m.child, p))
-				return augTRUE;
-		mem = next;
-	}
-	return augFALSE;
-}
-
-augExport augNoMemFunc *aug_set_nomem_func(augNoMemFunc *new_func)
-{
-	augNoMemFunc *old = mem_nomem_func;
-	DABNAME("aug_set_nomem_func");
-
-	mem_nomem_func = new_func;
-
-	DABTRACE("New nomem func %08lx, previous %08lx",
-		(unsigned long)mem_nomem_func, (unsigned long)old);
-	return old;
-}
-
-augExport augAllocStats *aug_alloc_stats(void)
-{
-	return &mem_stats;
-}
-
-augExport void *aug_alloc_loc(size_t size, void *parent, char *file, int line)
-{
-	void *alloc;
-	DABNAME("aug_alloc");
-
-	DAB("size %lu, parent %08lx [+%d %s]",
-		(unsigned long)size, (unsigned long)parent, line, file);
-
-	alloc = mem_alloc(size, parent, file, line);
-
-	DABL(80)("size %lu with header, caller mem at %08lx",
-		MEM_CROWN(alloc)->m.end - (char *)MEM_CROWN(alloc),
-		(unsigned long)alloc);
-
-	return alloc;
-}
-
-augExport void *aug_realloc_loc(size_t size, void *prev, char *file, int line)
-{
-	void *alloc;
-	size_t prev_size;
-	MemHead *mem, *par, *kid, *sib, *new;
-	DABNAME("aug_realloc");
-
-	if(!prev)
-		aug_abort(file, line, "Attempt to realloc a NULL pointer");
-
-	mem = MEM_CROWN(prev);
-	MEM_CHECK(mem, "previous alloc");
-
-	par = mem->m.parent;	MEM_CHECK(par, "realloc parent");
-	kid = mem->m.child;	MEM_CHECK(kid, "realloc child");
-	sib = mem->m.sibling;	MEM_CHECK(sib, "realloc sibling");
-
-	prev_size = (mem->m.end - (char *)mem) - sizeof (MemHead);
-
-	DAB("prior size %lu, new %lu [+%d %s]",
-				(unsigned long)prev_size, (unsigned long)size,
-				line, file);
-	DABL(80)("prior mem %08lx", (unsigned long)mem);
-
-	mem_stats.current_bytes_allocated += size - prev_size;
-	mem_stats.realloc_ops++;
-
-	size += sizeof (MemHead);
-
-	new = realloc(mem, size + sizeof (MemMagic));
-	if(!new)
-		mem_nomem(size, "aug_realloc", file, line);
-	new->m.end = (char *)new + size;
-
-	memcpy(new->m.end, (char *)&mem_magic, sizeof mem_magic);
-
-	if(par)
-	{
-		if(par->m.sibling == mem)
-			par->m.sibling = new;
-		else
-			par->m.child = new;
-	}
-	if(kid)
-		kid->m.parent = new;
-	if(sib)
-		sib->m.parent = new;
-
-	alloc = MEM_DECAPITATE(new);
-
-	DABL(80)("size %lu with header, caller mem at %08lx",
-			new->m.end - (char *)new, (unsigned long)alloc);
-
-	return alloc;
-}
-
-augExport void aug_free_loc(const void *alloc, char *file, int line)
-{
-	MemHead *mem, *par;
-	DABNAME("aug_free");
-
-	if(!alloc)
-		aug_abort(file, line, "Attempt to free a NULL pointer");
-
-	DAB("Freeing %08lx [+%d %s]", (unsigned long)alloc, line, file);
-
-	mem = MEM_CROWN(alloc);
-	MEM_CHECK(mem, "alloc to free");
-
-	par = mem->m.parent;
-	MEM_CHECK(par, "parent in free");
-
-	if(par)
-	{
-		if(par->m.sibling == mem)
-			par->m.sibling = mem->m.sibling;
-		else
-			par->m.child = mem->m.sibling;
-	}
-
-	if(mem->m.sibling)
-	{
-		mem->m.sibling->m.parent = par;
-		mem->m.sibling = 0;
-	}
-
-	mem_free(mem);
-}
-
-augExport void aug_foster_loc(void *alloc, void *parent, char *file, int line)
-{
-	MemHead *mem, *fpar, *ppar, *sib;
-	DABNAME("aug_foster");
-
-	DAB("Foster %08lx to %08lx [+%d %s]",
-						alloc, parent, line, file);
-
-	if(!alloc)
-		aug_abort(file, line, "Attempt to foster a NULL pointer");
-
-	mem = MEM_CROWN(alloc);
-	MEM_CHECK(mem, "alloc to foster");
-
-	if(parent)
-	{
-		fpar = MEM_CROWN(parent);
-		MEM_CHECK(fpar, "foster parent");
-	}
-	else
-		fpar = 0;
-
-	ppar = mem->m.parent; MEM_CHECK(ppar, "prior parent");
-	sib = mem->m.sibling; MEM_CHECK(ppar, "sibling in foster");
-
-	if(fpar == ppar)
-	{
-		DABTRACE("No change in parent (%08lx)", (unsigned long)fpar);
-		return;
-	}
-
-	if(mem == fpar)
-		aug_abort(file, line, "Attempt to adopt self");
-
-	/*
-	**  Check for incest - isnew parent actually our child?
-	*/
-	if(mem_find(mem->m.child, fpar))
-		aug_abort(file, line, "Attempt to adopt a parent");
-
-	/*
-	**  Leave home.
-	*/
-	if(!ppar)
-	{
-		DABBULK("Leaving orphanage");
-		if(mem->m.sibling)
-			mem->m.sibling->m.parent = 0;
-	}
-	else if(ppar->m.sibling == mem)
-	{
-		DABBULK("Older child");
-		ppar->m.sibling = mem->m.sibling;
-		if(ppar->m.sibling)
-			ppar->m.sibling->m.parent = ppar;
-	}
-	else
-	{
-		DABBULK("Youngest child");
-		ppar->m.child = mem->m.sibling;
-		if(ppar->m.child)
-			ppar->m.child->m.parent = ppar;
-	}
-
-	/*
-	**  Find new home.
-	*/
-	mem->m.parent = fpar;
-	if(fpar)
-	{
-		mem->m.sibling = fpar->m.child;
-		fpar->m.child = mem;
-		if(mem->m.sibling)
-			mem->m.sibling->m.parent = mem;
-	}
-	else
-		mem->m.sibling = 0;
-}
-
-augExport char *aug_strdup_loc(const char *str, void *parent, char *file,
-								int line)
-{
-	char *new;
-	size_t size;
-	DABNAME("aug_strdup");
-
-	if(!str)
-		aug_abort(file, line, "Attempt to duplicate a NULL string");
-
-	size = strlen(str)+1;
-	DAB("string length %lu [+%d %s]", (unsigned long)size, line, file);
-
-	new = mem_alloc(size, parent, file, line);
-
-	DABL(80)("size %lu with header, caller mem at %08lx",
-		MEM_CROWN(new)->m.end - (char *)MEM_CROWN(new),
-		(unsigned long)new);
-
-	strcpy(new, str);
-	return new;
-}
-
-augExport char **aug_vecdup_loc(char **vec, void *parent, char *file, int line)
-{
-	char **nv, **v, *c;
-	size_t size;
-	int vsize;
-	DABNAME("aug_vecdup");
-
-	if(!vec)
-		aug_abort(file, line, "Attempt to duplicate a NULL vector");
-
-	size = 0;
-	for(v = vec; *v; v++)
-		size += strlen(*v) + 1;
-	vsize = v - vec;
-	DABL(80)("%d elements, total string size %d", vsize, size);
-
-	vsize++;
-
-	nv = (char **)mem_alloc(vsize * sizeof *v + size, parent, file, line);
-	c = (char *)(nv + vsize);
-	for(v = nv; *vec; v++, vec++)
-	{
-		strcpy(c, *vec);
-		*v = c;
-		c += strlen(c) + 1;
-	}
-	*v = 0;
-
-	return nv;
-}
-
-#ifdef TEST
-static void nomem(size_t size, char *func, char *file, int line)
-{
-	fprintf(stderr, "\nNOMEM on %lu bytes via %s, called from %s line %d\n",
-		(unsigned long)size, func, file, line);
-	/*
-	**  Normally would exit from here, but might as well test the
-	**  default trap.
-	*/
-	return;
-}
-
-main(int argc, char **argv)
-{
-	int i;
-	void *par;
-	char *mem, *m, **v;
-
-	aug_setmodule(argv[0]);
-
-	printf("<MemHead size %lu> should equal <struct MemHead %lu>\n",
-		sizeof (MemHead), sizeof (struct MemHead));
-
-	par = aug_alloc(20, 0);
-	for(i = 0; i < 20; i++)
-	{
-		if(i == 10)
-			mem = aug_strdup("Hello, world\n", par);
-		else
-			(void)aug_alloc(3000, par);
-	}
-
-	mem = aug_realloc(10000, mem);
-
-	for(i = 0; i < 20; i++)
-	{
-		if(i == 10)
-			m = aug_strdup("Hello, world\n", mem);
-		else
-			(void)aug_alloc(3000, par);
-	}
-
-	v = aug_vecdup(argv, par);
-
-	printf("Program args:");
-	while(*v)
-	{
-		printf(" %s", *v++);
-		fflush(stdout);
-	}
-	printf("\n");
-
-	aug_foster(m, par);
-
-	if(argc > 1)
-	{
-		printf("Checking anti-incest test ... this should abort\n");
-		aug_foster(par, mem);
-	}
-
-	for(i = 0; i < 20; i++)
-	{
-		if(i == 10)
-			m = aug_strdup("Hello, world\n", mem);
-		else
-			(void)aug_alloc(3000, mem);
-	}
-
-	mem = aug_realloc(10000, mem);
-
-	aug_foster(m, par);
-
-	aug_free(mem);
-
-	printf("If you can read this, the test completed ok\n");
-	printf("Now testing NOMEM func - this should abort after a while ... ");
-	fflush(stdout);
-
-	aug_set_nomem_func(nomem);
-
-	while(m = aug_alloc(128*1024, par))
-		continue;
-
-	/* Should never get to here */
-
-	aug_free(par);
-
-	aug_exit(augEXIT_YES);
-}
-#endif

+ 0 - 95
modules/db_postgres/aug_alloc.h

@@ -1,95 +0,0 @@
-/*
- * $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 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
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
- *
- */
-/*
-** ________________________________________________________________________
-**
-**
-**                      $RCSfile$
-**                     $Revision$
-**
-**             Last change $Date$
-**           Last change $Author$
-**                        $State$
-**                       $Locker$
-**
-**               Original author: Andrew Fullford
-**
-**           Copyright (C) August Associates  1995
-**
-** ________________________________________________________________________
-*/
-
-/*  AM_TYPE: (INSTALL_INC)  */
-
-#ifndef AUG_ALLOC_H
-#define AUG_ALLOC_H
-
-#include <stdlib.h>
-
-typedef struct
-{
-	int estimated_overhead_per_alloc;	/* assumes malloc overhead
-						   is 8 bytes.  This is
-						   probably low */
-	unsigned long alloc_ops;		/* Total allocs since epoch */
-	unsigned long free_ops;			/* Total frees since epoch */
-	unsigned long realloc_ops;		/* Total reallocs since epoch */
-	unsigned long current_bytes_allocated;	/* Running allocation total */
-} augAllocStats;
-
-#define aug_alloc(s,p) aug_alloc_loc((s),(p),augDAB__FILE__,augDAB__LINE__)
-#define aug_realloc(s,m) aug_realloc_loc((s),(m),augDAB__FILE__,augDAB__LINE__)
-#define aug_strdup(s,p) aug_strdup_loc((s),(p),augDAB__FILE__,augDAB__LINE__)
-#define aug_vecdup(v,p) aug_vecdup_loc((v),(p),augDAB__FILE__,augDAB__LINE__)
-#define aug_free(m) aug_free_loc((m),augDAB__FILE__,augDAB__LINE__)
-#define aug_foster(m,p) aug_foster_loc((m),(p),augDAB__FILE__,augDAB__LINE__)
-
-typedef void augNoMemFunc(size_t size, char *func, char *file, int line);
-extern augNoMemFunc *aug_set_nomem_func(augNoMemFunc *new_func);
-
-extern void *aug_alloc_loc(size_t size, void *parent, char *file, int line);
-extern void *aug_realloc_loc(size_t size, void *prev, char *file, int line);
-extern char *aug_strdup_loc(const char *str, void *parent, char *file,
-								int line);
-extern char **aug_vecdup_loc(char **vec, void *parent, char *file, int line);
-extern void aug_free_loc(const void *mem, char *file, int line);
-extern void aug_foster_loc(void *mem, void *new_parent, char *file, int line);
-
-extern augAllocStats *aug_alloc_stats(void);
-
-#endif /* AUG_ALLOC_H */

+ 0 - 158
modules/db_postgres/aug_debug.h

@@ -1,158 +0,0 @@
-/*
- * $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 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
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
- *
- */
-/*
-** ________________________________________________________________________
-**
-**
-**                      $RCSfile$
-**                     $Revision$
-**
-**             Last change $Date$
-**           Last change $Author$
-**                        $State$
-**                       $Locker$
-**
-**               Original author: Andrew Fullford
-**
-**           Copyright (C) August Associates  1995
-**
-** ________________________________________________________________________
-*/
-
-/*  AM_TYPE: (INSTALL_INC)  */
-
-#ifndef AUG_DEBUG_H
-#define AUG_DEBUG_H
-
-
-#ifdef DEBUG
-
-#define augDAB_DEBUG	augTRUE
-
-#ifdef __FILE__
-#define augDAB__FILE__	__FILE__
-#else
-#define augDAB__FILE__	""
-#endif
-#ifdef __LINE__
-#define augDAB__LINE__	__LINE__
-#else
-#define augDAB__LINE__	0
-#endif
-
-#else
-
-#define augDAB_DEBUG	augFALSE
-#define augDAB__FILE__	""
-#define augDAB__LINE__	0
-
-#endif /* DEBUG */
-
-/*
-**  Debugging levels.
-**
-**  Each function is tagged with a debugging level.  The initial
-**  state is ``don't know'' which will trigger a match, a relatively
-**  slow operation.  The result of the match is recorded so future
-**  accesses will occur at integer test instruction speed.
-*/
-#define DAB_UNKNOWN	-1		/* Need to perform match */
-#define DAB_OFF		0		/* Matched, but debugs disabled */
-#define DAB_TRACE	25		/* Level implied by DABTRACE() macro */
-#define DAB_STD		50		/* Level implied by DAB() macro */
-#define DAB_BULK	75		/* Level implied by DABBULK() macro */
-
-#if augDAB_DEBUG == augTRUE
-
-#define DABNAME(name)	static char *aug_dab_func=name,			\
-				    aug_dab_file[]=augDAB__FILE__;	\
-			static short aug_dab_level=DAB_UNKNOWN, aug_dab_dummy
-
-#define DABLEVEL(lev)							\
-	(aug_dab_enabled &&						\
-	 ((aug_dab_level == DAB_UNKNOWN &&				\
-	   aug_dab_match(aug_dab_func,aug_dab_file,			\
-			 augDAB__LINE__,&aug_dab_level) >= (lev)) ||	\
-	  aug_dab_level >= (lev)) &&					\
-	 aug_dab_pushinfo(aug_dab_func,aug_dab_file,augDAB__LINE__))
-
-#define DABL(lev)	aug_dab_dummy = DABLEVEL(lev) && aug_dab_fmt
-#define DAB		DABL(DAB_STD)
-#define DABTRACE	DABL(DAB_TRACE)
-#define DABBULK		DABL(DAB_BULK)
-#define DABTEXT		aug_dab_text
-#define DABDUMP		aug_dab_dump
-
-#define DABSET(line)	aug_dab_set(line)
-#define DABRESET()	aug_dab_reset()
-#define DABLOAD(file)	aug_dab_load(file)
-
-extern augBool aug_dab_enabled;
-
-#else
-
-#ifdef __SUNPRO_C
-	#define DABL_NOP(...) /* ignore */
-#else
-	#define DABL_NOP(args...) /* ignore */
-#endif
-#define DABNAME(name)
-#define DABLEVEL(lev)	(augFALSE)
-#define DABL(lev)	 DABL_NOP
-#define DAB		DABL(0)
-#define DABTRACE	DABL(0)
-#define DABBULK		DABL(0)
-#define DABTEXT		DABL(0)
-#define DABDUMP		DABL(0)
-
-#define DABSET(line)
-#define DABRESET()
-#define DABLOAD(file)
-
-#endif /* augDAB_DEBUG */
-
-extern int aug_dab_match(char *func, char *file, int line, short *plevel);
-extern augBool aug_dab_pushinfo(char *func, char *file, int line);
-extern augBool aug_dab_fmt(char *fmt, ...);
-extern void aug_dab_text(char *text);
-extern void aug_dab_dump(char *data, int size);
-extern void aug_dab_reset(void);
-extern augBool aug_dab_set(char *line);
-extern void aug_dab_load(char *file);
-
-#endif /* AUG_DEBUG_H */

+ 0 - 154
modules/db_postgres/aug_std.h

@@ -1,154 +0,0 @@
-/*
- * $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 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
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
- *
- */
-/*
-** ________________________________________________________________________
-**
-**
-**                      $RCSfile$
-**                     $Revision$
-**
-**             Last change $Date$
-**           Last change $Author$
-**                        $State$
-**                       $Locker$
-**
-**               Original author: Andrew Fullford
-**
-**           Copyright (C) August Associates  1995
-**
-** ________________________________________________________________________
-*/
-
-/*  AM_TYPE: (INSTALL_INC)  */
-
-#ifndef AUG_STD_H
-#define AUG_STD_H
-
-#include "aug_sysdep.h"
-
-/*
-**  The rest of the libaug package has dependencies on these
-**  system includes.
-*/
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdarg.h>
-#include <time.h>
-
-/*
-**  Some boolean verbosity.
-*/
-#ifndef augBool
-#define augBool		char
-#endif
-#define augFALSE	0
-#define augTRUE		1
-
-/*
-**  Exit codes.
-**
-**  To just indicate success or failure, use augEXIT_OK and augEXIT_BAD.
-**  For more esoteric codes, use the others.  Their numbers are chosen
-**  so that they are unlikely to be generated by other applications, and
-**  also to avoid signal numbers or error counts.
-**
-**  Note that augEXIT_NOMEM will be generated by aug_alloc failures
-**  if they are not handled.
-*/
-#define augEXIT_YES	0
-#define augEXIT_NO	1
-#define augEXIT_NOMEM	99
-#define augEXIT_ABORT	(augEXIT_NOMEM-1)
-#define augEXIT_FATAL	(augEXIT_NOMEM-2)
-#define augEXIT_ERROR	(augEXIT_NOMEM-3)
-#define augEXIT_WARN	(augEXIT_NOMEM-4)
-#define augEXIT_USAGE	(augEXIT_NOMEM-5)
-
-/*
-**  Used as a tag to explicitly signify externally accessible symbols.
-**  I don't like leaving off the storage class; it's just too quiet.
-*/
-#define augExport
-
-/*
-**  Some utility prototypes.
-*/
-extern char *aug_cpy(char *tgt, char *src, int size);
-extern char *aug_cat(char *tgt, char *src, int size);
-extern char *aug_strip(char *tgt);
-extern char *aug_upper(char *tgt);
-extern char *aug_lower(char *tgt);
-extern int aug_strexpand(char *tgt, char *src, int tgt_size);
-extern char *aug_getsym(char *name, char *default_value);
-extern void aug_abort_va(char *file, int line, char *fmt, va_list ap);
-extern void aug_abort(char *file, int line, char *fmt, ...);
-extern void aug_exit(int exit_code);
-extern void aug_setmodule(char *name);
-extern char *aug_module(void);
-extern void aug_setenv(char *name, char *value);
-extern char *aug_tempnam(char *dir, char *pfx, void *parent);
-
-extern char *aug_hostname(void);
-extern char *aug_domain(void);
-extern char *aug_fqdn(char *addr);
-
-extern int aug_getport(char *name);
-
-extern void aug_sockopts(int s);
-
-extern int aug_signal_max(void);
-extern int aug_signal_num(char *signame);
-extern char *aug_signal_name(int sig);
-
-extern int aug_utc_offset(time_t tim);
-
-extern unsigned long aug_random(void);
-extern void aug_srandom(unsigned long seed);
-extern unsigned long aug_random_seed(int entropy);
-extern int aug_mkdir(char *name, int mode);
-
-extern char *aug_encrypt(char *type, char *key, char *string, void *parent);
-extern char *aug_decrypt(char *key, char *string, void *parent);
-
-extern char **aug_mxresolv(char *domain, int timeout, int retry, void *parent);
-extern char *aug_mxget(char *domain);
-
-#include "aug_debug.h"
-#include "aug_alloc.h"
-
-#endif /* AUG_STD_H */

+ 0 - 359
modules/db_postgres/aug_sysdep.h

@@ -1,359 +0,0 @@
-/*
- * $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 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
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
- *
- */
-/*
-** ________________________________________________________________________
-**
-**
-**                      $RCSfile$
-**                     $Revision$
-**
-**             Last change $Date$
-**           Last change $Author$
-**                        $State$
-**                       $Locker$
-**
-**               Original author: Andrew Fullford
-**
-**           Copyright (C) August Associates  1995
-**
-** ________________________________________________________________________
-*/
-
-/*  AM_TYPE: (INSTALL_INC)  */
-
-#ifndef AUG_SYSDEP_H
-#define AUG_SYSDEP_H
-
-/*
-**  As necessary, detect operating system, CPU, and compiler
-**  combinations, and establish defines that describe the
-**  characteristics and requirements for the combination.
-**
-**  As each special case is encountered elsewhere in the code,
-**  a new define should be added here for each affected system.
-**
-**  Defines take names like:
-**
-**	AUG_HAS_xxxx	System has capability xxxx
-**	AUG_NO_xxxx	System doesn't have capability xxxx
-**	AUG_BAD_xxxx	System has xxxx, but it's broken
-**
-**  Every system gets AUG_CONFIGURATION so we can reject misconfigured
-**  compiles.  This should be set to an os/cpu/compiler description.
-*/
-#undef AUG_CONFIGURATION
-
-/*
-**  This list should be maintained as the definitive list of capabilities.
-**  Add each new define here with a description and then in each system
-**  dependent section as appropriate.
-**
-**  Please stick to the "#undef" format -- the aug_sysdep.sh script
-**  uses these to report configurations.
-*/
-#undef AUG_HAS_SELECT_H		/* Select macros in <sys/select.h> instead of
-				   <sys/time.h> or <sys/time.h> */
-#undef AUG_BAD_FD_SET		/* FD_SET et al are broken (HP-UX) */
-
-#undef AUG_HAS_LP		/* SysV style "lp" and "lpstat" commands */
-#undef AUG_HAS_LP_REQUEST	/* Has the /usr/spool/lp/request directory.
-				   Probably only ever in HP-UX */
-#undef AUG_HAS_LPR		/* BSD style "lpr" and "/etc/printcap" */
-#undef AUG_NO_PUTENV		/* Use setenv() instead of putenv() */
-#undef AUG_HAS_PREAD		/* Has pread() (combined seek/read) */
-
-/* If neither AUG_HAS_RAND48 nor AUG_HAS_RANDOM, rand() will be used */
-#undef AUG_HAS_RAND48		/* Has lrand48/srand48 calls */
-#undef AUG_HAS_RANDOM		/* Has random/srandom calls */
-
-#undef AUG_HAS_SINCOS		/* -libm has a fast sincos() implementation */
-#undef AUG_NO_IOVEC		/* Some system may not have readv/writev */
-#undef AUG_NO_TIMES		/* Some system may not have times(2) */
-
-#undef AUG_HAS_PSAX		/* ps takes "-ax" arg to show all procs */
-#undef AUG_NO_TZARG		/* get/settimeofday takes no timezone arg */
-
-#undef AUG_NO_CRYPT_H		/* crypt(3) declared in unistd.h instead of
-				   crypt.h. */
-
-#undef AUG_NO_TERMIOS		/* System does not have the termios interface */
-
-#undef AUG_NO_TERMIO_H		/* No termio.h, only termios.h used */
-
-#undef AUG_NO_DB		/* System doesn't support UCB's db(3) */
-
-#undef AUG_NO_GETPAGESIZE	/* System does not have getpagesize() */
-#undef AUG_NO_PTHREADS		/* System does not have Posix Threads support */
-
-/*
-----------------------------------------------------------------------------
------ SGI Irix with sgi C --------------------------------------------------
-----------------------------------------------------------------------------
-*/
-
-#if defined(sgi) || defined(__sgi) || defined(__sgi__)
-
-#define AUG_HAS_LP
-#define AUG_CONFIGURATION	"SGI Irix with sgi C"
-#define AUG_HAS_RAND48
-
-typedef unsigned int augUInt32;
-
-#endif /* sgi */
-
-/*
-----------------------------------------------------------------------------
------ Sun Solaris 2.x on SPARC or x86, with SUNpro C or GCC ----------------
-----------------------------------------------------------------------------
-*/
-#if defined(sun) || defined(__sun) || defined(__sun__)
-
-#define AUG_HAS_LP
-#define AUG_HAS_PREAD
-#define AUG_HAS_RAND48
-
-#if defined(i386) || defined(__i386)
-
-#if defined(__GNUC__)
-#define AUG_CONFIGURATION	"Sun Solaris x86 with GCC"
-#else
-#define AUG_CONFIGURATION	"Sun Solaris x86 with SUNpro C"
-#endif
-
-typedef unsigned int augUInt32;
-
-#endif
-#if defined(x86_64) || defined(__x86_64)
-
-#if defined(__GNUC__)
-#define AUG_CONFIGURATION	"Sun Solaris x86_64 with GCC"
-#else
-#define AUG_CONFIGURATION	"Sun Solaris x86_64 with SUNpro C"
-#endif
-
-typedef unsigned int augUInt32;
-
-#endif
-
-#if defined(sparc) || defined(__sparc)
-#if defined(__svr4__) || defined(__SVR4)
-
-#if defined(__GNUC__)
-#define AUG_CONFIGURATION	"Sun Solaris 2.x SPARC with GCC"
-#else
-#define AUG_CONFIGURATION	"Sun Solaris 2.x SPARC with SUNpro C"
-#endif
-#endif /* svr4 */
-
-typedef unsigned int augUInt32;
-
-#endif /* sparc */
-
-#endif /* sun */
-
-/*
-----------------------------------------------------------------------------
------ Linux x86 with GCC ---------------------------------------------------
-----------------------------------------------------------------------------
-*/
-#ifdef __linux
-
-#define AUG_HAS_LPR
-#define AUG_HAS_RANDOM		/* Actually has AUG_HAS_RAND48 too */
-#define AUG_HAS_PSAX
-
-/* AUG_DEBIAN supplied on cc command line where appropriate */
-#ifndef AUG_DEBIAN
-#define AUG_NO_CRYPT_H
-#endif
-
-#if __GNUC__ <= 2 && __GNUC_MINOR__ <= 7
-/* Basically, assume this is a really of version of Linux -- ie "gomer" */
-#define AUG_NO_PTHREADS
-#endif
-
-#if defined(__i386)
-
-#if defined(__GNUC__)
-#define AUG_CONFIGURATION	"Linux x86 with GCC"
-#endif
-
-typedef unsigned int augUInt32;
-
-#endif /* x86 */
-#if defined(__x86_64)
-
-#if defined(__GNUC__)
-#define AUG_CONFIGURATION	"Linux x86_64 with GCC"
-#endif
-
-typedef unsigned int augUInt32;
-
-#endif /* x86-64 */
-#endif /* linux */
-
-/*
-----------------------------------------------------------------------------
------ FreeBSD x86 with GCC -------------------------------------------------
-----------------------------------------------------------------------------
-*/
-#ifdef __FreeBSD__
-
-#define AUG_HAS_LPR
-#define AUG_HAS_RANDOM
-#define AUG_HAS_PSAX
-#define AUG_HAS_PREAD
-#define AUG_NO_CRYPT_H
-#define AUG_NO_TERMIO_H
-#define AUG_NO_DB
-
-/*  FreeBSD lacks these error codes.  */
-#define ENODATA	ENOBUFS
-#define EPROTO	EPROTOTYPE
-#define EUNATCH	ENOPROTOOPT
-
-/*  FreeBSD lacks these termios codes.  */
-#define TCGETS	TIOCGETA
-#define TCSETS	TIOCSETA
-#define TCGETA	TIOCGETA
-#define TCSETA	TIOCSETA
-#define TCSETSW	TIOCSETAW
-#define TCFLSH	TIOCFLUSH
-#define termio termios
-
-#if defined(__i386)
-
-#if defined(__GNUC__)
-#define AUG_CONFIGURATION	"FreeBSD x86 with GCC"
-#endif
-
-typedef unsigned int augUInt32;
-
-#endif /* x86 */
-#if defined(__x86_64)
-
-#if defined(__GNUC__)
-#define AUG_CONFIGURATION	"FreeBSD x86_64 with GCC"
-#endif
-
-typedef unsigned int augUInt32;
-
-#endif /* x86_64 */
-#endif /* freebsd */
-
-/*
-----------------------------------------------------------------------------
------ HP-UX pa-risc with HP C ----------------------------------------------
-----------------------------------------------------------------------------
-*/
-
-#ifdef __hpux
-
-#define AUG_BAD_FD_SET			/* Not even fixed in HP-UX 10.x */
-#define AUG_HAS_LP
-#define AUG_HAS_LP_REQUEST
-#define AUG_HAS_RAND48
-#define AUG_HAS_SINCOS
-
-#if !defined(__GNUC__)
-#define AUG_CONFIGURATION	"HP-UX pa-risc with HP C"
-#endif
-
-typedef unsigned int augUInt32;
-
-#endif /* hpux */
-
-/*
-----------------------------------------------------------------------------
------ AIX Configuration with xlC -------------------------------------------
-----------------------------------------------------------------------------
-*/
-
-#ifdef _AIX
-
-#define AUG_HAS_LP
-#define AUG_HAS_LP_REQUEST
-#define AUG_HAS_SELECT_H
-#define AUG_HAS_RAND48
-#define AUG_NO_CRYPT_H
-
-#if !defined(__GNUC__)
-#define AUG_CONFIGURATION       "AIX Configuration with xlC"
-#endif
-
-typedef unsigned int augUInt32;
-
-#endif /* _AIX */
-
-/*
-----------------------------------------------------------------------------
------ Sun IUS with GCC (formerly Interactive Unix) -----------------------
-----------------------------------------------------------------------------
-*/
-
-/*
-**  This is only sufficient to build a basic libaug.a so selected
-**  utilities can be ported with relative ease.
-**
-**  None of the folio stuff builds (no unix domain sockets), so when
-**  collecting a fresh copy of $AUG/libaug, run "rm -f *fol*".
-*/
-#ifndef _AIX /* xlC can't handle these expressions */
-#if #system(svr3) && #cpu(i386)
-
-#define AUG_HAS_LP
-#define AUG_HAS_RAND48
-#define AUG_NO_CRYPT_H
-#define AUG_CONFIGURATION	"Sun IUS x86 with GCC"
-
-typedef unsigned int augUInt32;
-
-#include <sys/bsdtypes.h>
-
-#endif /* IUS */
-#endif /* ! _AIX */
-
-/*
-----------------------------------------------------------------------------
-*/
-
-#ifndef AUG_CONFIGURATION
-#error: os/cpu/compiler combination not configured in $Source$ $Revision$
-#endif
-
-#endif /* AUG_SYSDEP_H */

+ 0 - 131
modules/db_postgres/aug_util.c

@@ -1,131 +0,0 @@
-/*
- * $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 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
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
- *
- */
-/*
-** ________________________________________________________________________
-**
-**
-**                      $RCSfile$
-**                     $Revision$
-**
-**             Last change $Date$
-**           Last change $Author$
-**                        $State$
-**                       $Locker$
-**
-**               Original author: Andrew Fullford
-**
-**           Copyright (C) August Associates  1995
-**
-** ________________________________________________________________________
-*/
-
-#include "aug_std.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-
-static char *aug_module_name = 0;
-
-/*
-**  May eventually add ``on exit'' function calls.
-*/
-augExport void aug_exit(int exit_code)
-{
-	DABNAME("aug_exit");
-
-	DABTRACE("Exiting with code %d", exit_code);
-	exit(exit_code);
-}
-
-augExport void aug_abort_va(char *file, int line, char *fmt, va_list ap)
-{
-	DABNAME("aug_abort");
-
-	DAB("ABORT from +%d %s", line, file);
-
-	fflush(stdout);
-	fprintf(stderr, "\r\n\n");
-	if(aug_module_name)
-		fprintf(stderr, "%s: ", aug_module_name);
-	fprintf(stderr, "ABORT: ");
-	vfprintf(stderr, fmt, ap);
-	fprintf(stderr, " -- from +%d %s\r\n\n", line, file);
-
-	if(DABLEVEL(DAB_TRACE))
-	{
-		fprintf(stderr, "Program terminating via abort()\r\n\n");
-		abort();
-	}
-
-	aug_exit(augEXIT_ABORT);
-}
-
-augExport void aug_abort(char *file, int line, char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	aug_abort_va(file, line, fmt, ap);
-	va_end(ap);
-}
-
-/*
-**  WARNING -- don't use DABs below here, aug_debug.c calls this code.
-*/
-augExport void aug_setmodule(char *name)
-{
-	char *prog;
-
-	if(!name)
-		return;
-
-	if((prog = strrchr(name, '/')) ||
-	   (prog = strrchr(name, '\\')))
-		prog++;
-	else
-		prog = name;
-
-	aug_module_name = malloc(strlen(prog) + 1);
-	strcpy(aug_module_name, prog);
-}
-
-augExport char *aug_module(void)
-{
-	return (aug_module_name ? aug_module_name : "");
-}

+ 0 - 69
modules/db_postgres/con_postgres.h

@@ -1,69 +0,0 @@
-/*
- * $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 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
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
- *
- */
-
-
-#ifndef CON_POSTGRES_H
-#define CON_POSTGRES_H
-
-#include "libpq-fe.h"
-
-/*
- * Postgres specific connection data
- */
-struct con_postgres {
-	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 */
-	FILE *fp;	/* debug file output */
-	long tpid;	/* record pid of database opener in case one of */
-			/* the children try to close the database */
-};
-
-#define CON_SQLURL(db_con)    (((struct con_postgres*)((db_con)->tail))->sqlurl)
-#define CON_RESULT(db_con)    (((struct con_postgres*)((db_con)->tail))->res)
-#define CON_CONNECTION(db_con) (((struct con_postgres*)((db_con)->tail))->con)
-#define CON_FP(db_con)        (((struct con_postgres*)((db_con)->tail))->fp)
-#define CON_PID(db_con)       (((struct con_postgres*)((db_con)->tail))->tpid)
-#define CON_CONNECTED(db_con) (((struct con_postgres*)((db_con)->tail))->connected)
-
-#define PLOG(f,s) LOG(L_ERR, "PG[%d] %s %s\n",__LINE__,f,s)
-#define DLOG(f,s) LOG(L_INFO, "PG[%d] %s %s\n",__LINE__,f,s)
-
-#endif /* CON_POSTGRES_H */

+ 14 - 19
modules/db_postgres/db_con.c

@@ -1,11 +1,11 @@
-/*
- * $Id$
- *
- * POSTGRES module, portions of this code were templated using
- * the mysql module, thus it's similarity.
+/* 
+ * $Id$ 
  *
+ * Database connection related functions
  *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
  * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005 iptelorg GmbH
  *
  * This file is part of ser, a free SIP server.
  *
@@ -27,22 +27,14 @@
  * 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)
- *
  */
 
+
 #include <string.h>
+#include "../../db/db.h"
 #include "../../dprint.h"
 #include "../../mem/mem.h"
-#include "../../db/db_con.h"
-#include "defs.h"
-#include "con_postgres.h"
-#include "aug_std.h"
+
 
 /*
  * Store name of table that will be used by
@@ -50,8 +42,11 @@
  */
 int use_table(db_con_t* _h, const char* _t)
 {
-	if(CON_TABLE(_h))
-		aug_free(CON_TABLE(_h));
-	CON_TABLE(_h) = aug_strdup((char *) _t, _h);
+	if ((!_h) || (!_t)) {
+		LOG(L_ERR, "use_table: Invalid parameter value\n");
+		return -1;
+	}
+
+	CON_TABLE(_h) = _t;
 	return 0;
 }

+ 32 - 36
modules/db_postgres/db_mod.c

@@ -3,7 +3,9 @@
  *
  * Postgres module interface
  *
- * Copyright (C) 2001-2003 FhG Fokus
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005 iptelorg GmbH
  *
  * This file is part of ser, a free SIP server.
  *
@@ -26,58 +28,52 @@
  * 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);
-
+int connect_timeout = 0; /* Default is unlimited */
+int reconnect_attempts = 2; /* How many times should the module try to reconnect if
+			     * the connection is lost. 0 disables reconnecting */
 
 /*
- * MySQL database module interface
+ * Postgres database module interface
  */
-
-
 static cmd_export_t cmds[]={
-	{"db_use_table",   (cmd_function)use_table,     2, 0, 0},
-	{"db_init",        (cmd_function)db_init,       1, 0, 0},
-	{"db_close",       (cmd_function)db_close,      2, 0, 0},
-	{"db_query",       (cmd_function)db_query,      2, 0, 0},
-	{"db_raw_query",   (cmd_function)db_raw_query,  2, 0, 0},
-	{"db_free_result", (cmd_function)db_free_query, 2, 0, 0},
-	{"db_insert",      (cmd_function)db_insert,     2, 0, 0},
-	{"db_delete",      (cmd_function)db_delete,     2, 0, 0},
-	{"db_update",      (cmd_function)db_update,     2, 0, 0},
-	{0,0,0,0,0}
+	{"db_use_table",   (cmd_function)use_table,      2, 0, 0},
+	{"db_init",        (cmd_function)db_init,        1, 0, 0},
+	{"db_close",       (cmd_function)db_close,       2, 0, 0},
+	{"db_query",       (cmd_function)db_query,       2, 0, 0},
+	{"db_raw_query",   (cmd_function)db_raw_query,   2, 0, 0},
+	{"db_free_result", (cmd_function)db_free_result, 2, 0, 0},
+	{"db_insert",      (cmd_function)db_insert,      2, 0, 0},
+	{"db_delete",      (cmd_function)db_delete,      2, 0, 0},
+	{"db_update",      (cmd_function)db_update,      2, 0, 0},
+	{0, 0, 0, 0, 0}
 };
 
 
+/*
+ * Exported parameters
+ */
+static param_export_t params[] = {
+	{"connect_timeout",    INT_PARAM, &connect_timeout   },
+	{"reconnect_attempts", INT_PARAM, &reconnect_attempts},
+	{0, 0, 0}
+};
+
 
 struct module_exports exports = {	
 	"postgres",
 	cmds,
-	0,   /*  module parameters */
-
-	mod_init, /* module initialization function */
-	0,        /* response function*/
-	0,        /* destroy function */
-	0,        /* oncancel function */
-	0         /* per-child init function */
+	params,    /*  module parameters */
+        0,         /* module initialization function */
+	0,         /* response function*/
+	0,         /* destroy function */
+	0,         /* oncancel function */
+	0          /* per-child init function */
 };
 
-
-static int mod_init(void)
-{
-	fprintf(stderr, "postgres - initializing\n");
-	return 0;
-}

+ 12 - 18
modules/db_postgres/defs.h → modules/db_postgres/db_mod.h

@@ -1,11 +1,11 @@
-/*
- * $Id$
- *
- * POSTGRES module, portions of this code were templated using
- * the mysql module, thus it's similarity.
+/* 
+ * $Id$ 
  *
+ * Postgres module interface
  *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
  * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005 iptelorg GmbH
  *
  * This file is part of ser, a free SIP server.
  *
@@ -27,20 +27,14 @@
  * 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 _DB_MOD_H
+#define _DB_MOD_H
 
+/* 2000-01-01 00:00:00 +0000 as the value of time_t in UTC */
+#define PG_EPOCH_TIME ((long long)946684800)
 
-#ifndef DEFS_H
-#define DEFS_H
-
-#define PARANOID
-#define SQL_BUF_LEN 65535
+extern int connect_timeout;
+extern int reconnect_attempts;
 
-#endif /* DEFS_H */
+#endif /* _DB_MOD_H */

+ 0 - 228
modules/db_postgres/db_res.c

@@ -1,228 +0,0 @@
-/*
- * $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 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
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
- *
- */
-
-#include <stdlib.h>
-#include "../../db/db_res.h"
-#include "../../db/db_con.h"
-#include "../../dprint.h"
-#include "../../mem/mem.h"
-#include "defs.h"
-#include "con_postgres.h"
-#include "pg_type.h"
-#include "aug_std.h"
-
-int str2valp(db_type_t _t, db_val_t* _v, const char* _s, int _l, void *_p);
-
-/*
- * Create a new result structure and initialize it
- */
-db_res_t* new_result_pg(char *parent)
-{
-	db_res_t* r;
-
-	r = (db_res_t*)aug_alloc(sizeof(db_res_t), parent);
-
-	RES_NAMES(r) = 0;
-	RES_TYPES(r) = 0;
-	RES_COL_N(r) = 0;
-	RES_ROWS(r) = 0;
-	RES_ROW_N(r) = 0;
-
-	return r;
-}
-
-/*
- * Get and convert columns from a result
- */
-static inline int get_columns(db_con_t* _h, db_res_t* _r)
-{
-	int n, i;
-
-	n = PQnfields(CON_RESULT(_h));
-
-	if (!n) {
-		LOG(L_ERR, "get_columns(): No columns\n");
-		return -2;
-	}
-
-        RES_NAMES(_r) = (db_key_t*)aug_alloc(sizeof(db_key_t) * n, _r);
-	RES_TYPES(_r) = (db_type_t*)aug_alloc(sizeof(db_type_t) * n, _r);
-	RES_COL_N(_r) = n;
-
-	for(i = 0; i < n; i++) {
-		int ft;
-		RES_NAMES(_r)[i] = aug_strdup(PQfname(CON_RESULT(_h),i),
-			RES_NAMES(_r));
-		switch(ft = PQftype(CON_RESULT(_h),i))
-		{
-			case INT2OID:
-			case INT4OID:
-			case INT8OID:
-				RES_TYPES(_r)[i] = DB_INT;
-			break;
-
-			case FLOAT4OID:
-			case FLOAT8OID:
-			case NUMERICOID:
-				RES_TYPES(_r)[i] = DB_DOUBLE;
-			break;
-
-			case DATEOID:
-			case TIMESTAMPOID:
-			case TIMESTAMPTZOID:
-				RES_TYPES(_r)[i] = DB_DATETIME;
-			break;
-
-			case VARCHAROID:
-				RES_TYPES(_r)[i] = DB_STRING;
-			break;
-			
-			default:
-				LOG(L_ERR, "unknown type %d\n", ft);
-				RES_TYPES(_r)[i] = DB_STRING;
-			break;
-		}		
-	}
-	return 0;
-}
-
-/*
- * Convert a row from result into db API representation
- */
-int convert_row_pg(db_con_t* _h, db_res_t* _res, db_row_t* _r, char **row_buf,
-	char *parent)
-{
-	int i;
-
-        ROW_VALUES(_r) = (db_val_t*)aug_alloc(
-		sizeof(db_val_t) * RES_COL_N(_res), parent);
-	ROW_N(_r) = RES_COL_N(_res);
-
-	/* I'm not sure about this */
-	/* PQfsize() gives us the native length in the database, so */
-	/* an int would be 4, not the strlen of the value */
-	/* however, strlen of the value would be easy to strlen() */
-
-	for(i = 0; i < RES_COL_N(_res); i++) {
-		if (str2valp(RES_TYPES(_res)[i], &(ROW_VALUES(_r)[i]), 
-			    row_buf[i],
-			    PQfsize(CON_RESULT(_h),i),
-			    (void *) ROW_VALUES(_r)) < 0) {
-		LOG(L_ERR, "convert_row_pg(): Error while converting value\n");
-			return -3;
-		}
-	}
-	return 0;
-}
-/*
- * Convert rows from postgres to db API representation
- */
-static inline int convert_rows(db_con_t* _h, db_res_t* _r)
-{
-	int n, i, j, k;
-	char **row_buf, *s;
-	n = PQntuples(CON_RESULT(_h));
-	RES_ROW_N(_r) = n;
-	if (!n) {
-		RES_ROWS(_r) = 0;
-		return 0;
-	}
-	RES_ROWS(_r) = (struct db_row*)aug_alloc(sizeof(db_row_t) * n,_r);
-	j = RES_COL_N(_r);
-	row_buf = (char **) aug_alloc(sizeof(char *) * (j + 1), CON_SQLURL(_h));
-
-	/* j is the number of columns in the answer set */
-	/* n is the number of rows in the answer set */
-	for(i = 0; i < n; i++) {
-		for(k = 0; k < j; k++) {
-			if(PQgetisnull(CON_RESULT(_h), i, k)) {
-				/*
-				** I don't believe there is a NULL
-				** representation, so, I'll cheat
-				*/
-				s = "";
-			} else {
-				s = PQgetvalue(CON_RESULT(_h), i, k);
-			}
-			*(row_buf + k) = aug_strdup(s, row_buf);
-		}
-		*(row_buf + k) = (char *) 0;
-
-		/*
-		** ASSERT: row_buf contains an entire row in strings
-		*/
-		if (convert_row_pg(_h, _r, &(RES_ROWS(_r)[i]), row_buf,
-			(char *) RES_ROWS(_r)) < 0) {
-			LOG(L_ERR, "convert_rows(): Error converting row #%d\n",
-				i);
-			RES_ROW_N(_r) = i;
-			aug_free(row_buf);
-			return -4;
-		}
-	}
-	aug_free(row_buf);
-	return 0;
-}
-
-/*
- * Fill the structure with data from database
- */
-int convert_result(db_con_t* _h, db_res_t* _r)
-{
-	if (get_columns(_h, _r) < 0) {
-		LOG(L_ERR, "convert_result(): Error getting column names\n");
-		return -2;
-	}
-
-	if (convert_rows(_h, _r) < 0) {
-		LOG(L_ERR, "convert_result(): Error while converting rows\n");
-		return -3;
-	}
-	return 0;
-}
-
-
-/*
- * Release memory used by a result structure
- */
-int free_result(db_res_t* _r)
-{
-	aug_free(_r);
-
-	return 0;
-}

+ 0 - 145
modules/db_postgres/db_utils.c

@@ -1,145 +0,0 @@
-/*
- * $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 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
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
- *
- */
-
-#define _GNU_SOURCE /* To avoid strptime warning */
-
-#include <string.h>
-#include <time.h>
-#include "db_utils.h"
-#include "defs.h"
-
-char* trim(char* _s);
-
-/*
- * SQL URL parser
- */
-int parse_sql_url(char* _url, char** _user, char** _pass, 
-		  char** _host, char** _port, char** _db)
-{
-	char* slash, *dcolon, *at, *db_slash;
-
-	*_user = '\0';
-	*_pass = '\0';
-	*_host = '\0';
-	*_port = '\0';
-	*_db   = '\0';
-
-	     /* Remove any leading and trailing spaces and tab */
-	_url = trim(_url);
-
-	if (strlen(_url) < 6) return -1;
-
-	if (*_url == '\0') return -2; /* Empty string */
-
-	slash = strchr(_url, '/');
-	if (!slash) return -3;   /* Invalid string, slashes not found */
-
-	if ((*(++slash)) != '/') {  /* Invalid URL, 2nd slash not found */
-		return -4;
-	}
-
-	slash++;
-
-	at = strchr(slash, '@');
-
-	db_slash = strrchr(slash, '/');
-	if (db_slash <= at)
-		db_slash = NULL;
-	if (db_slash) {
-		*db_slash++ = '\0';
-		*_db = trim(db_slash);
-	}
-
-	if (!at) {
-		dcolon = strchr(slash, ':');
-		if (dcolon) {
-			*dcolon++ = '\0';
-			*_port = trim(dcolon);
-		}
-		*_host = trim(slash);
-	} else {
-		dcolon = strchr(slash, ':');
-	        *at++ = '\0';
-		if (dcolon) {
-			*dcolon++ = '\0';
-			if (dcolon < at) {   /* user:passwd */
-				*_pass = trim(dcolon);
-				dcolon = strchr(at, ':');
-				if (dcolon) {  /* host:port */
-					*dcolon++ = '\0';
-					*_port = trim(dcolon);
-				}
-			} else {            /* host:port */
-				*_port = trim(dcolon);
-			}
-		}
-		*_host = trim(at);
-		*_user = trim(slash);
-	}
-
-	return 0;
-}
-
-
-/*
- * Remove any tabs and spaces from the beginning and the end of
- * a string
- */
-char* trim(char* _s)
-{
-	int len;
-	char* end;
-
-	     /* Null pointer, there is nothing to do */
-	if (!_s) return _s;
-
-	     /* Remove spaces and tabs from the beginning of string */
-	while ((*_s == ' ') || (*_s == '\t')) _s++;
-
-	len = strlen(_s);
-
-        end = _s + len - 1;
-
-	     /* Remove trailing spaces and tabs */
-	while ((*end == ' ') || (*end == '\t')) end--;
-	if (end != (_s + len - 1)) {
-		*(end+1) = '\0';
-	}
-
-	return _s;
-}

+ 0 - 358
modules/db_postgres/db_val.c

@@ -1,358 +0,0 @@
-/*
- * $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 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
- *
- * ---
- *
- * 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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "../../db/db_val.h"
-#include "../../dprint.h"
-#include "defs.h"
-#include "db_utils.h"
-#include "con_postgres.h"
-#include "aug_std.h"
-
-
-char *strptime(const char *s, const char *format, struct tm *tm);
-
-/*
- * Convert a string to integer
- */
-static inline int str2int(const char* _s, int* _v)
-{
-#ifdef PARANOID
-	if ((!_s) || (!_v)) {
-		LOG(L_ERR, "str2int(): Invalid parameter value\n");
-		return -1;
-	}
-#endif
-	*_v = atoi(_s);
-	return 0;
-}
-
-
-/*
- * Convert a string to double
- */
-static inline int str2double(const char* _s, double* _v)
-{
-#ifdef PARANOID
-	if ((!_s) || (!_v)) {
-		LOG(L_ERR, "str2double(): Invalid parameter value\n");
-		return -1;
-	}
-#endif
-	*_v = atof(_s);
-	return 0;
-}
-
-
-/* 
- * Convert a string to time_t
- */
-static inline int str2time(const char* _s, time_t* _v)
-{
-	struct tm t;
-#ifdef PARANOID
-	if ((!_s) || (!_v)) {
-		LOG(L_ERR, "str2time(): Invalid parameter value\n");
-		return -1;
-	}
-#endif
-
-	memset(&t, '\0', sizeof(struct tm));
-	strptime(_s,"%Y-%m-%d %H:%M:%S %z",&t);
-
-	     /* Daylight saving information got lost in the database
-	      * so let mktime to guess it. This eliminates the bug when
-	      * contacts reloaded from the database have different time
-	      * of expiration by one hour when daylight saving is used
-	      */ 
-	t.tm_isdst = -1;   
-	*_v = mktime(&t);
-
-	return 0;
-}
-
-
-/*
- * Convert an integer to string
- */
-static inline int int2str(int _v, char* _s, int* _l)
-{
-#ifdef PARANOID
-	if ((!_s) || (!_l) || (!*_l)) {
-		LOG(L_ERR, "int2str(): Invalid parameter value\n");
-		return -1;
-	}
-#endif
-	*_l = snprintf(_s, *_l, "%-d", _v);
-	return 0;
-}
-
-
-/*
- * Convert a double to string
- */
-static inline int double2str(double _v, char* _s, int* _l)
-{
-#ifdef PARANOID
-	if ((!_s) || (!_l) || (!*_l)) {
-		LOG(L_ERR, "double2str(): Invalid parameter value\n");
-		return -1;
-	}
-#endif
-	*_l = snprintf(_s, *_l, "%-10.2f", _v);
-	return 0;
-}
-
-
-/*
- * Convert time_t to string
- */
-static inline int time2str(time_t _v, char* _s, int* _l)
-{
-	struct tm *t;
-	int bl;
-#ifdef PARANOID
-	if ((!_s) || (!_l) || (*_l < 2))  {
-		LOG(L_ERR, "Invalid parameter value\n");
-		return -1;
-	}
-#endif
-
-	t = localtime(&_v);
-
-	if((bl=strftime(_s,(size_t)(*_l)-1,"'%Y-%m-%d %H:%M:%S %z'",t))>0)
-		*_l = bl;
-	
-	return 0;
-}
-
-/*
- * Does not copy strings
- */
-int str2valp(db_type_t _t, db_val_t* _v, const char* _s, int _l, void *_p)
-{
-	char dbuf[256];
-#ifdef PARANOID
-	if (!_v) {
-		LOG(L_ERR, "str2valp(): Invalid parameter value\n");
-		return -1;
-	}
-#endif
-
-	if (!_s) {
-		DLOG("str2valp", "got a null value");
-		VAL_TYPE(_v) = _t;
-		VAL_NULL(_v) = 1;
-		return 0;
-	}
-
-	switch(_t) {
-	case DB_INT:
-	case DB_BITMAP:
-		sprintf(dbuf, "got int %s", _s);
-		DLOG("str2valp", dbuf);
-		if (str2int(_s, &VAL_INT(_v)) < 0) {
-			LOG(L_ERR, "str2valp(): Error while converting integer value from string\n");
-			return -2;
-		} else {
-			VAL_TYPE(_v) = DB_INT;
-			return 0;
-		}
-		break;
-	
-	case DB_DOUBLE:
-		sprintf(dbuf, "got double %s", _s);
-		DLOG("str2valp", dbuf);
-		if (str2double(_s, &VAL_DOUBLE(_v)) < 0) {
-			LOG(L_ERR, "str2valp(): Error while converting double value from string\n");
-			return -3;
-		} else {
-			VAL_TYPE(_v) = DB_DOUBLE;
-			return 0;
-		}
-		break;
-
-	case DB_STRING:
-		sprintf(dbuf, "got string %s", _s);
-		DLOG("str2valp", dbuf);
-
-		VAL_STRING(_v) = aug_strdup(_s, _p);
-		VAL_TYPE(_v) = DB_STRING;
-
-		return 0;
-
-	case DB_STR:
-		VAL_STR(_v).s = aug_alloc(_l + 1, _p);
-		memcpy(VAL_STR(_v).s, _s,  _l);
-		VAL_STR(_v).s[_l] = (char) 0;
-		VAL_STR(_v).len = _l;
-		VAL_TYPE(_v) = DB_STR;
-
-		sprintf(dbuf, "got len string %d %s", _l, _s);
-		DLOG("str2valp", dbuf);
-
-		return 0;
-
-	case DB_DATETIME:
-		sprintf(dbuf, "got time %s", _s);
-		DLOG("str2valp", dbuf);
-		if (str2time(_s, &VAL_TIME(_v)) < 0) {
-			PLOG("str2valp", "error converting datetime");
-			return -4;
-		} else {
-			VAL_TYPE(_v) = DB_DATETIME;
-			return 0;
-		}
-		break;
-
-	case DB_BLOB:
-
-		VAL_STR(_v).s = aug_alloc(_l + 1, _p);
-		memcpy(VAL_STR(_v).s, _s,  _l);
-		VAL_STR(_v).s[_l] = (char) 0;
-		VAL_STR(_v).len = _l;
-		VAL_TYPE(_v) = DB_BLOB;
-
-		sprintf(dbuf, "got blob %d", _l);
-		DLOG("str2valp", dbuf);
-
-		return 0;
-	}
-	return -5;
-}
-
-
-/*
- * Used when converting result from a query
- */
-int val2str(db_val_t* _v, char* _s, int* _len)
-{
-	int l;
-
-#ifdef PARANOID
-	if ((!_v) || (!_s) || (!_len) || (!*_len)) {
-		LOG(L_ERR, "val2str(): Invalid parameter value\n");
-		return -1;
-	}
-#endif
-	if (VAL_NULL(_v)) {
-		*_len = snprintf(_s, *_len, "NULL");
-		return 0;
-	}
-	
-	switch(VAL_TYPE(_v)) {
-	case DB_INT:
-		if (int2str(VAL_INT(_v), _s, _len) < 0) {
-			LOG(L_ERR, "val2str(): Error while converting string to int\n");
-			return -2;
-		} else {
-			return 0;
-		}
-		break;
-
-	case DB_DOUBLE:
-		if (double2str(VAL_DOUBLE(_v), _s, _len) < 0) {
-			LOG(L_ERR, "val2str(): Error while converting string to double\n");
-			return -3;
-		} else {
-			return 0;
-		}
-		break;
-
-	case DB_STRING:
-		l = strlen(VAL_STRING(_v));
-		LOG(L_ERR, "val2str(): converting %s, %d\n", VAL_STRING(_v), l);
-		if (*_len < (l + 3)) {
-			LOG(L_ERR, "val2str(): Destination buffer too short\n");
-			return -4;
-		} else {
-			*_s++ = '\'';
-			memcpy(_s, VAL_STRING(_v), l);
-			*(_s + l) = '\'';
-			*(_s + l + 1) = '\0'; /* FIXME */
-			*_len = l + 2;
-			return 0;
-		}
-		break;
-
-	case DB_STR:
-		l = VAL_STR(_v).len;
-		if (*_len < (l + 3)) {
-			LOG(L_ERR, "val2str(): Destination buffer too short %d\n", *_len);
-			return -5;
-		} else {
-			*_s++ = '\'';
-			memcpy(_s, VAL_STR(_v).s, l);
-			*(_s + l) = '\'';
-			*(_s + l + 1) = '\0';
-			*_len = l + 2;
-			return 0;
-		}
-		break;
-
-	case DB_DATETIME:
-		if (time2str(VAL_TIME(_v), _s, _len) < 0) {
-			LOG(L_ERR, "val2str(): Error while converting string to time_t\n");
-			return -6;
-		} else {
-			return 0;
-		}
-		break;
-
-	case DB_BLOB:
-		l = VAL_BLOB(_v).len;
-		if (*_len < (l * 2 + 3)) {
-			LOG(L_ERR, "val2str(): Destination buffer too short\n");
-			return -7;
-		} else {
-			     /* WRITE ME */
-			return 0;
-		}			
-		break;
-
-	default:
-		DBG("val2str(): Unknown data type\n");
-		return -7;
-	}
-	return -8;
-}

+ 875 - 661
modules/db_postgres/dbase.c

@@ -1,11 +1,11 @@
-/*
- * $Id$
- *
- * POSTGRES module, portions of this code were templated using
- * the mysql module, thus it's similarity.
+/* 
+ * $Id$ 
  *
+ * MySQL module core functions
  *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
  * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005 iptelorg GmbH
  *
  * This file is part of ser, a free SIP server.
  *
@@ -27,795 +27,1009 @@
  * 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)
- *
  */
 
-#define MAXCOLUMNS	512
-
+#include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <stdio.h>
-#include "../../dprint.h"
+#include <time.h>
+#include <libpq-fe.h>
+#include <netinet/in.h>
 #include "../../mem/mem.h"
-#include "db_utils.h"
-#include "defs.h"
+#include "../../dprint.h"
+#include "../../db/db_pool.h"
+#include "../../ut.h"
+#include "pg_con.h"
+#include "pg_type.h"
+#include "db_mod.h"
+#include "res.h"
 #include "dbase.h"
-#include "con_postgres.h"
-#include "aug_std.h"
-
-long getpid();
-
-static char sql_buf[SQL_BUF_LEN];
-
-static int submit_query(db_con_t* _h, const char* _s);
-static int connect_db(db_con_t* _h, const char* _db_url);
-static int disconnect_db(db_con_t* _h);
-static int free_query(db_con_t* _h);
-
-/*
-** connect_db	Connect to a database
-**
-**	Arguments :
-**		db_con_t *	as previously supplied by db_init()
-**		char *_db_url	the database to connect to
-**
-**	Returns :
-**		0 upon success
-**		negative number upon failure
-**
-**	Notes :
-**		If currently connected, a disconnect is done first
-**		if this process did the connection, otherwise the
-**		disconnect is not done before the new connect.
-**		This is important, as the process that owns the connection
-**		should clean up after itself.
-*/
-
-static int connect_db(db_con_t* _h, const char* _db_url)
+
+
+#define SELECTALL "select * "
+#define SELECT    "select "
+#define FROM      "from "
+#define ORDER     "order by "
+#define WHERE     "where "
+#define AND       " and "
+#define INSERT    "insert into "
+#define VALUES    ") values ("
+#define DELETE    "delete from "
+#define UPDATE    "update "
+#define SET       "set "
+
+
+struct pg_params {
+	int n;
+	int cur;
+	const char** data;
+	int* len;
+	int* formats;
+};
+
+
+static void free_pg_params(struct pg_params* ptr)
+{
+	if (!ptr) return;
+	if (ptr->data) pkg_free(ptr->data);
+	if (ptr->len) pkg_free(ptr->len);
+	if (ptr->formats) pkg_free(ptr->formats);
+	pkg_free(ptr);
+}
+
+
+static struct pg_params* new_pg_params(int n)
 {
-	char* user, *password, *host, *port, *database;
-
-	if(! _h)
-	{
-		PLOG("connect_db", "must pass db_con_t!");
-		return(-1);
-	}
-
-	if(CON_CONNECTED(_h))
-	{
-		DLOG("connect_db", "disconnect first!");
-		disconnect_db(_h);
-	}
-
-	/*
-	** CON_CONNECTED(_h) is now 0, set by disconnect_db()
-	*/
-
-	/*
-	** Note :
-	** Make a scratch pad copy of given SQL URL.
-	** all memory allocated to this connection is rooted
-	** from this.
-	** This is an important concept.
-	** as long as you always allocate memory using the function:
-	** mem = aug_alloc(size, CON_SQLURL(_h)) or
-	** str = aug_strdup(string, CON_SQLURL(_h))
-	** where size is the amount of memory, then in the future
-	** when CON_SQLURL(_h) is freed (in the function disconnect_db())
-	** all other memory allocated in this manner is freed.
-	** this will keep memory leaks from happening.
-	*/
-	CON_SQLURL(_h) = aug_strdup((char *) _db_url, (char *) _h);
-
-	/*
-	** get the connection parameters parsed from the db_url string
-	** it looks like: postgres://username:userpass@dbhost:dbport/dbname
-	** username/userpass : name and password for the database
-	** dbhost :            the host name or ip address hosting the database
-	** dbport :            the port to connect to database on
-	** dbname :            the name of the database
-	*/
-	if(parse_sql_url(CON_SQLURL(_h),
-		&user,&password,&host,&port,&database) < 0)
-	{
-		char buf[256];
-		sprintf(buf, "Error while parsing %s", _db_url);
-		PLOG("connect_db", buf);
-
-		aug_free(CON_SQLURL(_h));
-		return -3;
-	}
-
-	/*
-	** finally, actually connect to the database
-	*/
-	CON_CONNECTION(_h) =
-		PQsetdbLogin(host,port,NULL,NULL,database,user, password);
-
-	if(CON_CONNECTION(_h) == 0
-	    || PQstatus(CON_CONNECTION(_h)) != CONNECTION_OK)
-	{
-		PLOG("connect_db", PQerrorMessage(CON_CONNECTION(_h)));
-		PQfinish(CON_CONNECTION(_h));
-		aug_free(CON_SQLURL(_h));
-		return -4;
-	}
-
-	CON_PID(_h) = getpid();
-
-	/*
-	** all is well, database was connected, we can now submit_query's
-	*/
-	CON_CONNECTED(_h) = 1;
+	struct pg_params* ptr;
+	
+	ptr = (struct pg_params*)pkg_malloc(sizeof(struct pg_params));
+	if (!ptr) goto error;
+
+	ptr->formats = (int*)pkg_malloc(sizeof(int) * n);
+	if (!ptr->formats) goto error;
+
+	ptr->data = (const char**)pkg_malloc(sizeof(const char*) * n);
+	if (!ptr->data) goto error;
+
+	ptr->len = (int*)pkg_malloc(sizeof(int) * n);
+	if (!ptr->len) goto error;
+	
+	memset((char*)ptr->data, 0, sizeof(const char*) * n);
+	memset(ptr->len, 0, sizeof(int) * n);
+	ptr->n = n;
+	ptr->cur = 0;
+	return ptr;
+
+ error:
+	LOG(L_ERR, "postgres:new_pg_params: No memory left\n");
+	free_pg_params(ptr);
 	return 0;
 }
 
 
-/*
-** disconnect_db	Disconnect a database
-**
-**	Arguments :
-**		db_con_t *	as previously supplied by db_init()
-**
-**	Returns :
-**		0 upon success
-**		negative number upon failure
-**
-**	Notes :
-**		All memory associated with CON_SQLURL is freed.
-**		
-*/
-
-static int disconnect_db(db_con_t* _h)
+static inline int params_add(struct pg_params* p, db_con_t* con, db_val_t* vals, int n)
 {
-	if(! _h)
-	{
-		DLOG("disconnect_db", "null db_con_t, ignored!\n");
-		return(0);
-	}
-
-	/*
-	** free lingering memory tree if it exists
-	*/
-	if(CON_SQLURL(_h))
-	{
-		aug_free(CON_SQLURL(_h));
-		CON_SQLURL(_h) = (char *) 0;
-	}
-
-	/*
-	** ignore if there is no current connection
-	*/
-	if(CON_CONNECTED(_h) != 1)
-	{
-		DLOG("disconnect_db", "not connected, ignored!\n");
-		return 0;
-	}
+	int i, i1, i2;
+	db_val_t* val;
 
-	/*
-	** make sure we are trying to close a connection that was opened
-	** by our process ID
-	*/
-	if(CON_PID(_h) == getpid())
-	{
-		PQfinish(CON_CONNECTION(_h));
-		CON_CONNECTED(_h) = 0;
+	if (!p) {
+		LOG(L_ERR, "postgres:params_add: Invalid parameter value\n");
+		return -1;
 	}
-	else
-	{
-		DLOG("disconnect_db",
-			"attempt to release connection not owned, ignored!\n");
+
+	if (p->cur + n > p->n) {
+		LOG(L_ERR, "postgres:params_add: Arrays too short (bug in postgres module)\n");
+		return -1;
 	}
 
+	for(i = 0; i < n; i++) {
+		val = &vals[i];
+		p->formats[p->cur] = 1;
+		switch(val->type) {
+		case DB_INT:      
+			if (!val->nul) {
+				val->val.int_val = ntohl(val->val.int_val);
+				p->data[p->cur] = (const char*)&val->val.int_val;
+				p->len[p->cur] = 4;
+			}
+			break;
+			
+		case DB_DOUBLE:
+			if (!val->nul) {
+				     /* Change the byte order of 8-byte value to network
+				      * byte order if necessary
+				      */
+				i1 = htonl(val->val.int8_val >> 32);
+				i2 = htonl(val->val.int8_val & 0xffffffff);
+				val->val.int_val = i1;
+				(&val->val.int_val)[1] = i2;
+				p->data[p->cur] = (const char*)&val->val.int_val;
+				p->len[p->cur] = 8;
+			}
+			break;
+			
+		case DB_STRING:
+			p->formats[p->cur] = 0;
+			if (!val->nul) {
+				p->data[p->cur] = val->val.string_val;
+			}
+			break;
+			
+		case DB_STR:
+			if (!val->nul) {
+				p->data[p->cur] = val->val.str_val.s;
+				p->len[p->cur] = val->val.str_val.len;
+			}
+			break;
+			
+		case DB_DATETIME:
+			if (!val->nul) {
+				if (CON_FLAGS(con) & PG_INT8_TIMESTAMP) {
+					val->val.int8_val = ((long long)val->val.time_val - PG_EPOCH_TIME) * 1000000;
+				} else {
+					val->val.double_val = (double)val->val.time_val - (double)PG_EPOCH_TIME;
+					
+				}
+				i1 = htonl(val->val.int8_val >> 32);
+				i2 = htonl(val->val.int8_val & 0xffffffff);
+				val->val.int_val = i1;
+				(&val->val.int_val)[1] = i2;
+				p->data[p->cur] = (const char*)&val->val.int_val;
+				p->len[p->cur] = 8;
+			}
+			break;
+			
+		case DB_BLOB:
+			if (!val->nul) {
+				p->data[p->cur] = val->val.blob_val.s;
+				p->len[p->cur] = val->val.blob_val.len;
+			}
+			break;
+			
+		case DB_BITMAP: 
+			if (!val->nul) {
+				(&val->val.int_val)[1] = htonl(val->val.int_val);
+				val->val.int_val = htonl(32);
+				p->data[p->cur] = (const char*)&val->val.int_val;
+				p->len[p->cur] = 8;
+			}
+			break;
+		}
+		
+		p->cur++;
+	}
+	
 	return 0;
 }
 
-/*
-** db_init	initialize database for future queries
-**
-**	Arguments :
-**		char *_sqlurl;	sql database to open
-**
-**	Returns :
-**		db_con_t * NULL upon error
-**		db_con_t * if successful
-**
-**	Notes :
-**		db_init must be called prior to any database
-**		functions.
-*/
-
-db_con_t *db_init(const char* _sqlurl)
+static inline void free_params(struct pg_params* p)
 {
+	if (p->data) pkg_free(p->data);
+	if (p->len) pkg_free(p->len);
+	if (p->formats) pkg_free(p->formats);
+}
+
+
+/*
+ * Initialize database module
+ * No function should be called before this
+ */
+db_con_t* db_init(const char* url)
+{
+	struct db_id* id;
+	struct pg_con* con;
 	db_con_t* res;
-	void* t;
 
-	DLOG("db_init", "entry");
+	id = 0;
+	res = 0;
 
-	/*
-	** this is the root memory for this database connection.
-	*/
-	res = aug_alloc(sizeof(db_con_t), 0);
-	memset(res, 0, sizeof(db_con_t));
-	t = aug_alloc(sizeof(struct con_postgres), (char*)res);
-	res->tail = (unsigned long) t;
-	memset((struct con_postgres*)res->tail, 0, sizeof(struct con_postgres));
+	if (!url) {
+		LOG(L_ERR, "postgres:db_init: Invalid parameter value\n");
+		return 0;
+	}
+
+	res = pkg_malloc(sizeof(db_con_t) + sizeof(struct pg_con*));
+	if (!res) {
+		LOG(L_ERR, "postgres:db_init: No memory left\n");
+		return 0;
+	}
+	memset(res, 0, sizeof(db_con_t) + sizeof(struct pg_con*));
 
-	if (connect_db(res, _sqlurl) < 0)
-	{
-		PLOG("db_init", "Error while trying to open database, FATAL\n");
-		aug_free(res);
-		return((db_con_t *) 0);
+	id = new_db_id(url);
+	if (!id) {
+		LOG(L_ERR, "postgres:db_init: Cannot parse URL '%s'\n", url);
+		goto err;
 	}
 
+	     /* Find the connection in the pool */
+	con = (struct pg_con*)pool_get(id);
+	if (!con) {
+		DBG("postgres:db_init: Connection '%s' not found in pool\n", url);
+		     /* Not in the pool yet */
+		con = new_connection(id);
+		if (!con) {
+			goto err;
+		}
+		pool_insert((struct pool_con*)con);
+	} else {
+		DBG("postgres:db_init: Connection '%s' found in pool\n", url);
+	}
+
+	res->tail = (unsigned long)con;
 	return res;
+
+ err:
+	if (id) free_db_id(id);
+	if (res) pkg_free(res);
+	return 0;
 }
 
 
-/*
-** db_close	last function to call when db is no longer needed
-**
-**	Arguments :
-**		db_con_t * the connection to shut down, as supplied by db_init()
-**
-**	Returns :
-**		(void)
-**
-**	Notes :
-**		All memory and resources are freed.
-*/
-
-void db_close(db_con_t* _h)
+/*
+ * Shut down database module
+ * No function should be called after this
+ */
+void db_close(db_con_t* handle)
 {
-	DLOG("db_close", "entry");
-	if(! _h)
-	{
-		PLOG("db_close", "no handle passed, ignored");
+	struct pool_con* con;
+
+	if (!handle) {
+		LOG(L_ERR, "postgres:db_close: Invalid parameter value\n");
 		return;
 	}
 
-	disconnect_db(_h);
-	aug_free(_h);
+	con = (struct pool_con*)handle->tail;
+	if (pool_remove(con) != 0) {
+		free_connection((struct pg_con*)con);
+	}
 
+	pkg_free(handle);
 }
 
-/*
-** submit_query	run a query
-**
-**	Arguments :
-**		db_con_t *	as previously supplied by db_init()
-**		char *_s	the text query to run
-**
-**	Returns :
-**		0 upon success
-**		negative number upon failure
-*/
-
-static int submit_query(db_con_t* _h, const char* _s)
-{	
-	int rv;
-
-	/*
-	** this bit of nonsense in case our connection get screwed up 
-	*/
-	switch(rv = PQstatus(CON_CONNECTION(_h)))
-	{
-		case CONNECTION_OK: break;
-		case CONNECTION_BAD:
-			PLOG("submit_query", "connection reset");
-			PQreset(CON_CONNECTION(_h));
-		break;
-	}
 
-	/*
-	** free any previous query that is laying about
-	*/
-	if(CON_RESULT(_h))
-	{
-		free_query(_h);
-	}
-
-	/*
-	** exec the query
-	*/
-	CON_RESULT(_h) = PQexec(CON_CONNECTION(_h), _s);
-
-	rv = 0;
-	if(PQresultStatus(CON_RESULT(_h)) == 0)
-	{
-		PLOG("submit_query", "initial failure, FATAL");
-		/* 
-		** terrible error??
-		*/
-		rv = -3;
-	}
-	else
-	{
-		/*
-		** the query ran, get the status
-		*/
-		switch(PQresultStatus(CON_RESULT(_h)))
-		{
-			case PGRES_EMPTY_QUERY: rv = -9; break;
-			case PGRES_COMMAND_OK: rv = 0; break;
-			case PGRES_TUPLES_OK: rv = 0; break;
-			case PGRES_COPY_OUT: rv = -4; break;
-			case PGRES_COPY_IN: rv = -5; break;
-			case PGRES_BAD_RESPONSE: rv = -6; break;
-			case PGRES_NONFATAL_ERROR: rv = -7; break;
-			case PGRES_FATAL_ERROR: rv = -8; break;
-			default: rv = -2; break;
-		}
-	}
-	if(rv < 0)
-	{
-		/*
-		** log the error
-		*/
-		char buf[256];
-		sprintf(buf, "query '%s', result '%s'\n",
-			_s, PQerrorMessage(CON_CONNECTION(_h)));
-		PLOG("submit_query", buf);
-	}
+static int calc_param_len(start, num)
+{
+	int max, len, order;
 
-	return(rv);
-}
+	if (!num) return 0;
 
-/*
-** free_query	clear the db channel and clear any old query result status
-**
-**	Arguments :
-**		db_con_t *	as previously supplied by db_init()
-**
-**	Returns :
-**		0 upon success
-**		negative number upon failure
-*/
-
-static int free_query(db_con_t* _h)
-{
-	if(CON_RESULT(_h))
-	{
-		PQclear(CON_RESULT(_h));
-		CON_RESULT(_h) = 0;
+	max = start + num - 1;
+	len = num; /* $ */
+	
+	order = 0;
+	while(max) {
+		order++;
+		max /= 10;
 	}
 
-	return 0;
+	return len + order * num;
 }
 
-/*
-** db_free_query	free the query and free the result memory
-**
-**	Arguments :
-**		db_con_t *	as previously supplied by db_init()
-**		db_res_t *	the result of a query
-**
-**	Returns :
-**		0 upon success
-**		negative number upon failure
-*/
-
-int db_free_query(db_con_t* _h, db_res_t* _r)
+/*
+ * Append a constant string, uses sizeof to figure the length
+ * of the string
+ */
+#define append(buf, ptr)                                  \
+    do {                                                  \
+        if ((buf).len < (sizeof(ptr) - 1)) goto shortbuf; \
+        memcpy((buf).s, (ptr), sizeof(ptr) - 1);          \
+        (buf).s += sizeof(ptr) - 1;                       \
+        (buf).len -= sizeof(ptr) - 1;                     \
+    } while(0);
+
+
+/*
+ * Append zero terminated string, uses strlen to obtain the
+ * length of the string
+ */
+#define append_str(buf, op)                  \
+    do {                                     \
+	int len;                             \
+        len = strlen(op);                    \
+        if ((buf).len < len) goto shortbuf;  \
+        memcpy((buf).s, (op), len);          \
+        (buf).s += len;                      \
+        (buf).len -= len;                    \
+    } while(0);
+
+
+/*
+ * Append a parameter, accepts the number of the
+ * parameter to be appended
+ */
+#define append_param(buf, num)                  \
+    do {                                        \
+        const char* c;                          \
+        int len;                                \
+        c = int2str((num), &len);               \
+        if ((buf).len < len + 1) goto shortbuf; \
+        *(buf).s='$'; (buf).s++; (buf).len--;   \
+        memcpy((buf).s, c, len);                \
+        (buf).s += len; (buf).len -= len;       \
+    } while(0); 
+
+
+/*
+ * Calculate the length of buffer needed to hold the insert query
+ */
+static unsigned int calc_insert_len(db_con_t* con, db_key_t* keys, int n)
 {
-	free_query(_h);
-	free_result(_r);
+	int i;
+	unsigned int len;
 
-	return 0;
+	if (!n) return 0;
+
+	len = sizeof(INSERT) - 1;
+	len += strlen(CON_TABLE(con)); /* Table name */
+	len += 2; /* _( */
+	for(i = 0; i < n; i++) {
+		len += strlen(keys[i]); /* Key names */
+	}
+	len += n - 1; /* , */
+	len += sizeof(VALUES);
+	len += calc_param_len(1, n);
+	len += n - 1;
+	len += 1; /* ) */
+	return len;
 }
 
-/*
-** begin_transaction	begin transaction
-**
-**	Arguments :
-**		db_con_t *	as previously supplied by db_init()
-**		char *		this is just in case an error message
-**				is printed, we will know which query
-**				was going to be run, giving us a code debug hint
-**
-**	Returns :
-**		0 upon success
-**		negative number upon failure
-**
-**	Notes :
-**		This function may be called with a messed up communication
-**		channel.  Therefore, alot of this function is dealing with
-**		that.  Wen the layering gets corrected later this stuff
-**		should continue to work correctly, it will just be
-**		way to defensive.
-*/
-
-static int begin_transaction(db_con_t * _h, char *_s)
+
+/*
+ * Calculate the length of buffer needed to hold the delete query
+ */
+static unsigned int calc_delete_len(db_con_t* con, db_key_t* keys, int n)
 {
-	PGresult *mr;
-	int rv;
-
-	/*
-	** Note:
-	**  The upper layers of code may attempt a transaction
-	**  before opening or having a valid connection to the
-	**  database.  We try to sense this, and open the database
-	**  if we have the sqlurl in the _h structure.  Otherwise,
-	**  all we can do is return an error.
-	*/
-
-	if(_h)
-	{
-		if(CON_CONNECTED(_h))
-		{
-			mr = PQexec(CON_CONNECTION(_h), "BEGIN");
-			if(!mr || PQresultStatus(mr) != PGRES_COMMAND_OK)
-			{
-				/*
-				** We get here if the connection to the
-				** db is corrupt, which can happen a few
-				** different ways, but all of them are
-				** related to the parent process forking,
-				** or being forked.
-				*/
-				PLOG("begin_transaction","corrupt connection");
-				CON_CONNECTED(_h) = 0;
-			}
-			else
-			{
-				/*
-				** this is the normal way out.
-				** the transaction ran fine.
-				*/
-				PQclear(mr);
-				return(0);
-			}
-		}
-		else
-		{
-			DLOG("begin_transaction", "called before db_init");
+	int i;
+	unsigned int len;
+
+	len = sizeof(DELETE) - 1;
+	len += strlen(CON_TABLE(con));
+	if (n) {
+		len += 1; /* _ */
+		len += sizeof(WHERE) - 1;
+		len += n * 2; /* <= */
+		len += (sizeof(AND) - 1) * (n - 1);
+		for(i = 0; i < n; i++) {
+			len += strlen(keys[i]);
 		}
+		len += calc_param_len(1, n);
+	}
+	return len;
+}
+
+static unsigned int calc_select_len(db_con_t* con, db_key_t* cols, db_key_t* keys, int n, int ncol, db_key_t order)
+{
+	int i;
+	unsigned int len;
 
-		/*
-		** if we get here we have a corrupt db connection,
-		** but we probably have a valid db_con_t structure.
-		** attempt to open the db.
-		*/
-
-		if((rv = connect_db(_h, CON_SQLURL(_h))) != 0)
-		{
-			/*
-			** our attempt to fix the connection failed
-			*/
-			char buf[256];
-			sprintf(buf, "no connection, FATAL %d!", rv);
-			PLOG("begin_transaction",buf);
-			return(rv);
+	if (!cols) {
+		len = sizeof(SELECTALL) - 1;
+	} else {
+		len = sizeof(SELECT);
+		for(i = 0; i < ncol; i++) {
+			len += strlen(cols[i]);
 		}
+		len += ncol - 1; /* , */
+		len++; /* space */
 	}
-	else
-	{
-		PLOG("begin_transaction","must call db_init first!");
-		return(-1);
+	len += sizeof(FROM) - 1;
+	len += strlen(CON_TABLE(con));
+	len += 1; /* _ */
+	if (n) {
+		len += sizeof(WHERE) - 1;
+		len += n * 2; /* <= */
+		len += (sizeof(AND) - 1) * (n - 1);
+		for(i = 0; i < n; i++) {
+			len += strlen(keys[i]);
+		}
+		len += calc_param_len(1, n);
+		len++; /* space */
 	}
-
-	/*
-	** we get here if the database connection was corrupt,
-	** i didn't want to use recursion ...
-	*/
-
-	mr = PQexec(CON_CONNECTION(_h), "BEGIN");
-	if(!mr || PQresultStatus(mr) != PGRES_COMMAND_OK)
-	{
-		char buf[256];
-		sprintf("FATAL %s, '%s'!\n",
-			PQerrorMessage(CON_CONNECTION(_h)), _s);
-		PLOG("begin_transaction", buf);
-		return(-1);
+	if (order) {
+		len += sizeof(ORDER);
+		len += strlen(order);
 	}
+	return len;
+}
 
-	DLOG("begin_transaction", "db channel reset successful");
+static unsigned int calc_update_len(db_con_t* con, db_key_t* ukeys, db_key_t* keys, int un, int n)
+{
+	int i;
+	unsigned int len;
 
-	PQclear(mr);
-	return(0);
+	if (!un) return 0;
+
+	len = sizeof(UPDATE) - 1;
+	len += strlen(CON_TABLE(con));
+	len += 1; /* _ */
+	len += sizeof(SET) - 1;
+	len += un;  /* = */
+	for (i = 0; i < un; i++) {
+		len += strlen(ukeys[i]);
+	}
+	len += calc_param_len(1, un);
+	len += un; /* , and last space */
+	
+	if (n) {
+		len += sizeof(WHERE) - 1;
+		len += n * 2; /* <= */
+		len += (sizeof(AND) - 1) * (n - 1);
+		for(i = 0; i < n; i++) {
+			len += strlen(keys[i]);
+		}
+		len += calc_param_len(1 + un, n);
+	}
+	return len;
 }
 
-/*
-** commit_transaction	any begin_transaction must be terminated with this
-**
-**	Arguments :
-**		db_con_t *	as previously supplied by db_init()
-**
-**	Returns :
-**		0 upon success
-**		negative number upon failure
-*/
-
-static int commit_transaction(db_con_t * _h)
+
+char* print_insert(db_con_t* con, db_key_t* keys, int n)
 {
-	PGresult *mr;
+	unsigned int len;
+	int i;
+	char* s;
+	str p;
 
-	mr = PQexec(CON_CONNECTION(_h), "COMMIT");
-	if(!mr || PQresultStatus(mr) != PGRES_COMMAND_OK)
-	{
-		PLOG("commit_transaction", "error");
-		return -1;
+	if (!n || !keys) {
+		LOG(L_ERR, "postgres:print_insert: Nothing to insert\n");
+		return 0;
+	}
+
+	len = calc_insert_len(con, keys, n);
+	
+	s = (char*)pkg_malloc(len + 1);
+	if (!s) {
+		LOG(L_ERR, "postgres:print_insert: Unable to allocate %d of memory\n", len);
+		return 0;
+	}
+	p.s = s;
+	p.len = len;
+	
+	append(p, INSERT);
+	append_str(p, CON_TABLE(con));
+	append(p, " (");
+
+	append_str(p, keys[0]);
+	for(i = 1; i < n; i++) {
+		append(p, ",");
+		append_str(p, keys[i]);
+	}
+	append(p, VALUES);
+
+	append_param(p, 1);
+	for(i = 1; i < n; i++) {
+		append(p, ",");
+		append_param(p, i + 1);
 	}
-	PQclear(mr);
-	return(0);
+	append(p, ")");
+	*p.s = '\0';
+	return s;
+
+ shortbuf:
+	LOG(L_ERR, "postgres:print_insert: Buffer too short (bug in postgres module)\n");
+	pkg_free(s);
+	return 0;
 }
 
-/*
- * Print list of columns separated by comma
- */
-static int print_columns(char* _b, int _l, db_key_t* _c, int _n)
+
+
+char* print_select(db_con_t* con, db_key_t* cols, db_key_t* keys, int n, int ncol, db_op_t* ops, db_key_t order)
 {
+	unsigned int len;
 	int i;
-	int res = 0;
-	for(i = 0; i < _n; i++) {
-		if (i == (_n - 1)) {
-			res += snprintf(_b + res, _l - res, "%s ", _c[i]);
+	char* s;
+	str p;
+
+	len = calc_select_len(con, cols, keys, n, ncol, order);
+
+	s = (char*)pkg_malloc(len + 1);
+	if (!s) {
+		LOG(L_ERR, "postrgres:print_select: Unable to allocate %d of memory\n", len);
+		return 0;
+	}
+	p.s = s;
+	p.len = len;
+	
+	if (!cols || !ncol) {
+		append(p, SELECTALL);
+	} else {
+		append(p, SELECT);
+		append_str(p, cols[0]);
+		for(i = 1; i < ncol; i++) {
+			append(p, ",");
+			append_str(p, cols[i]);
+		}
+		append(p, " ");
+	}
+	append(p, FROM);
+	append_str(p, CON_TABLE(con));
+	append(p, " ");
+	if (n) {
+		append(p, WHERE);
+		append_str(p, keys[0]);
+		if (ops) {
+			append_str(p, *ops);
+			ops++;
 		} else {
-			res += snprintf(_b + res, _l - res, "%s,", _c[i]);
+			append(p, "=");
+		}
+		append_param(p, 1);
+		for(i = 1; i < n; i++) {
+			append(p, AND);
+			append_str(p, keys[i]);
+			if (ops) {
+				append_str(p, *ops);
+				ops++;
+			} else {
+				append(p, "=");
+			}
+			append_param(p, i + 1);
 		}
+		append(p, " ");
 	}
-	return res;
+	if (order) {
+		append(p, ORDER);
+		append_str(p, order);
+	}
+
+	*p.s = '\0'; /* Zero termination */
+	return s;
+
+ shortbuf:
+	LOG(L_ERR, "postgres:print_select: Buffer too short (bug in postgres module)\n");
+	pkg_free(s);
+	return 0;
 }
 
 
-/*
- * Print list of values separated by comma
- */
-static int print_values(char* _b, int _l, db_val_t* _v, int _n)
+char* print_delete(db_con_t* con, db_key_t* keys, db_op_t* ops, int n)
 {
-	int i, res = 0, l;
-
-	for(i = 0; i < _n; i++) {
-		l = _l - res;
-/*		LOG(L_ERR, "%d sizes l = _l - res %d = %d - %d\n", i, l,_l,res);
-*/
-		if (val2str(_v + i, _b + res, &l) < 0) {
-			LOG(L_ERR,
-			 "print_values(): Error converting value to string\n");
-			return 0;
+	unsigned int len;
+	int i;
+	char* s;
+	str p;
+
+	len = calc_delete_len(con, keys, n);
+
+	s = (char*)pkg_malloc(len + 1);
+	if (!s) {
+		LOG(L_ERR, "postrgres:print_delete: Unable to allocate %d of memory\n", len);
+		return 0;
+	}
+	p.s = s;
+	p.len = len;
+
+	append(p, DELETE);
+	append_str(p, CON_TABLE(con));
+	append(p, " ");
+	if (n) {
+		append(p, WHERE);
+		append_str(p, keys[0]);
+		if (ops) {
+			append_str(p, *ops);
+			ops++;
+		} else {
+			append(p, "=");
 		}
-		res += l;
-		if (i != (_n - 1)) {
-			*(_b + res) = ',';
-			res++;
+		append_param(p, 1);
+		for(i = 1; i < n; i++) {
+			append(p, AND);
+			append_str(p, keys[i]);
+			if (ops) {
+				append_str(p, *ops);
+				ops++;
+			} else {
+				append(p, "=");
+			}
+			append_param(p, i + 1);
 		}
 	}
-	return res;
+
+	*p.s = '\0';
+	return s;
+
+ shortbuf:
+	LOG(L_ERR, "postgres:print_delete: Buffer too short (bug in postgres module)\n");
+	pkg_free(s);
+	return 0;
 }
 
 
-/*
- * Print where clause of SQL statement
- */
-static int print_where(char* _b, int _l, db_key_t* _k,
-	db_op_t* _o, db_val_t* _v, int _n)
+char* print_update(db_con_t* con, db_key_t* ukeys, db_key_t* keys, db_op_t* ops, int un, int n)
 {
+	unsigned int len, param_no;
+	char* s;
 	int i;
-	int res = 0;
-	int l;
-	for(i = 0; i < _n; i++) {
-		if (_o) {
-			res += snprintf(_b + res, _l - res, "%s%s",
-				_k[i], _o[i]);
+	str p;
+
+	if (!un) {
+		LOG(L_ERR, "postgres:print_update: Nothing to update\n");
+		return 0;
+	}
+
+	param_no = 1;
+	len = calc_update_len(con, ukeys, keys, un, n);
+
+	s = (char*)pkg_malloc(len + 1);
+	if (!s) {
+		LOG(L_ERR, "postrgres:print_update: Unable to allocate %d of memory\n", len);
+		return 0;
+	}
+	p.s = s;
+	p.len = len;
+
+	append(p, UPDATE);
+	append_str(p, CON_TABLE(con));
+	append(p, " " SET);
+
+	append_str(p, ukeys[0]);
+	append(p, "=");
+	append_param(p, param_no++);
+
+	for(i = 1; i < un; i++) {
+		append(p, ",");
+		append_str(p, ukeys[i]);
+		append(p, "=");
+		append_param(p, param_no++);
+	}
+	append(p, " ");
+
+	if (n) {
+		append(p, WHERE);
+		append_str(p, keys[0]);
+		if (ops) {
+			append_str(p, *ops);
+			ops++;
 		} else {
-			res += snprintf(_b + res, _l - res, "%s=", _k[i]);
+			append(p, "=");
 		}
-		l = _l - res;
-		val2str(&(_v[i]), _b + res, &l);
-		res += l;
-		if (i != (_n - 1)) {
-			res += snprintf(_b + res, _l - res, " AND ");
+		append_param(p, param_no++);
+		
+		for(i = 1; i < n; i++) {
+			append(p, AND);
+			append_str(p, keys[i]);
+			if (ops) {
+				append_str(p, *ops);
+				ops++;
+			} else {
+				append(p, "=");
+			}
+			append_param(p, param_no++);
 		}
-	}
-	return res;
-}
+	}	
 
+	*p.s = '\0';
+	return s;
+
+ shortbuf:
+	LOG(L_ERR, "postgres:print_update: Buffer too short (bug in postgres module)\n");
+	pkg_free(s);
+	return 0; 
+}
 
 /*
- * Print set clause of update SQL statement
+ * Return values: 1 Query failed, bad connection
+ *                0 Query succeeded
+ *               -1 Query failed due to some other reason
  */
-static int print_set(char* _b, int _l, db_key_t* _k,
-	db_val_t* _v, int _n)
+static int submit_query(db_res_t** res, db_con_t* con, const char* query, struct pg_params* params)
 {
-	int i;
-	int res = 0;
-	int l;
-	for(i = 0; i < _n; i++) {
-		res += snprintf(_b + res, _l - res, "%s=", _k[i]);
-		l = _l - res;
-		val2str(&(_v[i]), _b + res, &l);
-		res += l;
-		if (i != (_n - 1)) {
-			if ((_l - res) >= 1) {
-				*(_b + res++) = ',';
-			}
+	PGresult* pgres;
+
+	DBG("postgres: Executing '%s'\n", query);
+	if (params) {
+	        pgres = PQexecParams(CON_CONNECTION(con), query,
+				     params->n, 0,
+				     params->data, params->len,
+				     params->formats, 1);
+	} else {
+		pgres = PQexecParams(CON_CONNECTION(con), query, 0, 0, 0, 0, 0, 1);
+	}
+	switch(PQresultStatus(pgres)) {
+	case PGRES_EMPTY_QUERY:
+		LOG(L_ERR, "postgres:submit_query:BUG: db_raw_query received an empty query\n");
+		goto error;
+		
+	case PGRES_COMMAND_OK:
+	case PGRES_NONFATAL_ERROR:
+	case PGRES_TUPLES_OK:
+		     /* Success */
+		break;
+		
+	case PGRES_COPY_OUT:
+	case PGRES_COPY_IN:
+		LOG(L_ERR, "postgres:submit_query: Unsupported transfer mode\n");
+		goto error;
+
+	case PGRES_BAD_RESPONSE:
+	case PGRES_FATAL_ERROR:
+		LOG(L_ERR, "postgres: Error: %s", PQresultErrorMessage(pgres));
+		if (PQstatus(CON_CONNECTION(con)) != CONNECTION_BAD) {
+				LOG(L_ERR, "postgres: Unknown error occurred, giving up\n");
+				goto error;
 		}
+		LOG(L_ERR, "postgres:submit_query: Bad connection\n");
+		PQclear(pgres);
+		return 1;
 	}
-	return res;
+
+	if (res) {
+		*res = new_result(pgres);
+		if (!(*res)) goto error;
+	} else {
+		PQclear(pgres);
+	}
+	return 0;
+
+ error:
+	PQclear(pgres);
+	return -1;
+}
+
+
+static int reconnect(db_con_t* con)
+{
+	int attempts_left = reconnect_attempts;
+	while(attempts_left) {
+		LOG(L_ERR, "postgres: Trying to recover the connection\n");
+		PQreset(CON_CONNECTION(con));
+		if (PQstatus(CON_CONNECTION(con)) == CONNECTION_OK) {
+			LOG(L_ERR, "postgres: Successfuly reconnected\n");
+			return 0;
+		}
+		LOG(L_ERR, "postgres: Reconnect attempt failed\n");
+		attempts_left--;
+	}
+	LOG(L_ERR, "postgres: No more reconnect attempts left, giving up\n");
+	return -1;
 }
 
 
 /*
  * Query table for specified rows
- * _h: 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
+ * con:   structure representing database connection
+ * keys:  key names
+ * ops:   operators
+ * vals:  values of the keys that must match
+ * cols:  column names to return
+ * n:     number of key=values pairs to compare
+ * ncol:  number of columns to return
+ * order: order by the specified column
+ * res:   query result
  */
-int db_query(db_con_t* _h, db_key_t* _k, db_op_t* _op,
-	     db_val_t* _v, db_key_t* _c, int _n, int _nc,
-	     db_key_t _o, db_res_t** _r)
+int db_query(db_con_t* con, db_key_t* keys, db_op_t* ops,
+	     db_val_t* vals, db_key_t* cols, int n, int ncols,
+	     db_key_t order, db_res_t** res)
 {
-	int off, rv;
-	if (!_c) {
-		off = snprintf(sql_buf, SQL_BUF_LEN,
-			"select * from %s ", CON_TABLE(_h));
-	} else {
-		off = snprintf(sql_buf, SQL_BUF_LEN, "select ");
-		off += print_columns(sql_buf + off, SQL_BUF_LEN - off, _c, _nc);
-		off += snprintf(sql_buf + off, SQL_BUF_LEN - off,
-			"from %s ", CON_TABLE(_h));
-	}
-	if (_n) {
-		off += snprintf(sql_buf + off, SQL_BUF_LEN - off, "where ");
-		off += print_where(sql_buf + off, SQL_BUF_LEN - off,
-			_k, _op, _v, _n);
-	}
-	if (_o) {
-		off += snprintf(sql_buf + off, SQL_BUF_LEN - off,
-			"order by %s", _o);
-	}
-
-	if(begin_transaction(_h, sql_buf)) return(-1);
-	if (submit_query(_h, sql_buf) < 0) {
-		LOG(L_ERR, "db_query(): Error while submitting query\n");
-		return -2;
-	}
-	rv = get_result(_h, _r);
-	free_query(_h);
-	commit_transaction(_h);
-	return(rv);
+	int ret;
+	char* select;
+	struct pg_params* params;
+	
+	params = 0;
+	select = 0;
+
+	if (!con) {
+		LOG(L_ERR, "postgres:db_query: Invalid parameter value\n");
+		return -1;
+	}
+
+	select = print_select(con, cols, keys, n, ncols, ops, order);
+	if (!select) goto err;
+
+	params = new_pg_params(n);
+	if (!params) goto err;
+	if (params_add(params, con, vals, n) < 0) goto err;
+
+	do {
+		ret = submit_query(res, con, select, params);
+		if (ret < 0) goto err;                       /* Unknown error, bail out */
+		if (ret > 0) {                               /* Disconnected, try to reconnect */
+			if (reconnect(con) < 0) goto err;    /* Failed to reconnect */
+			else continue;                       /* Try one more time (ret is > 0) */
+		}
+	} while(ret != 0);
+	
+	if (res && convert_result(*res, con) < 0) {
+		free_result(*res);
+		goto err;
+	}
+
+	free_pg_params(params);
+	pkg_free(select);
+	return 0;
+
+ err:
+	if (params) free_pg_params(params);
+	if (select) pkg_free(select);
+	return -1;
 }
 
 
 /*
  * Execute a raw SQL query
  */
-int db_raw_query(db_con_t* _h, char* _s, db_res_t** _r)
+int db_raw_query(db_con_t* con, char* query, db_res_t** res)
 {
-	int rv;
+	int ret;
 
-	if(begin_transaction(_h, sql_buf)) return(-1);
-	if (submit_query(_h, _s) < 0) {
-		LOG(L_ERR, "db_raw_query(): Error while submitting query\n");
-		return -2;
+	if (!con || !query) {
+		LOG(L_ERR, "postgres:db_raw_query: Invalid parameter value\n");
+		return -1;
+	}
+
+	do {
+		ret = submit_query(res, con, query, 0);
+		if (ret < 0) return -1;                      /* Unknown error, bail out */
+		if (ret > 0) {                               /* Disconnected, try to reconnect */
+			if (reconnect(con) < 0) return -1;   /* Failed to reconnect */
+			else continue;                       /* Try one more time (ret is > 0) */
+		}
+	} while(ret != 0);
+	
+	if (res && (convert_result(*res, con) < 0)) {
+		free_result(*res);
+		return -1;
 	}
-	rv = get_result(_h, _r);
-	free_query(_h);
-	commit_transaction(_h);
-	return(rv);
+	return 0;
 }
 
+
 /*
- * Retrieve result set
+ * Insert a row into specified table
+ * con:  structure representing database connection
+ * keys: key names
+ * vals: values of the keys
+ * n:    number of key=value pairs
  */
-int get_result(db_con_t* _h, db_res_t** _r)
+int db_insert(db_con_t* con, db_key_t* keys, db_val_t* vals, int n)
 {
-	*_r = new_result_pg(CON_SQLURL(_h));
+	int ret;
+	char* insert;
+	struct pg_params* params;
 
-	if (!CON_RESULT(_h)) {
-		LOG(L_ERR, "get_result(): error");
-		free_result(*_r);
-		*_r = 0;
-		return -3;
+	if (!con || !keys || !vals || !n) {
+		LOG(L_ERR, "postgres:db_insert: Invalid parameter value\n");
+		return -1;
 	}
 
-        if (convert_result(_h, *_r) < 0) {
-		LOG(L_ERR, "get_result(): Error while converting result\n");
-		free_result(*_r);
-		*_r = 0;
+	params = 0;
+	insert = 0;
 
-		return -4;
-	}
+	insert = print_insert(con, keys, n);
+	if (!insert) goto err;
+
+	params = new_pg_params(n);
+	if (!params) goto err;
+	if (params_add(params, con, vals, n) < 0) goto err;
 
+	do {
+		ret = submit_query(0, con, insert, params);
+		if (ret < 0) goto err;                       /* Unknown error, bail out */
+		if (ret > 0) {                               /* Disconnected, try to reconnect */
+			if (reconnect(con) < 0) goto err;    /* Failed to reconnect */
+			else continue;                       /* Try one more time (ret is > 0) */
+		}
+	} while(ret != 0);
+	
+	free_pg_params(params);
+	pkg_free(insert);
 	return 0;
+
+ err:
+	if (params) free_pg_params(params);
+	if (insert) pkg_free(insert);
+	return -1;
 }
 
+
 /*
- * Insert a row into specified table
- * _h: structure representing database connection
- * _k: key names
- * _v: values of the keys
- * _n: number of key=value pairs
+ * Delete a row from the specified table
+ * con : structure representing database connection
+ * keys: key names
+ * ops : operators
+ * vals: values of the keys that must match
+ * n   : number of key=value pairs
  */
-int db_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n)
+int db_delete(db_con_t* con, db_key_t* keys, db_op_t* ops, db_val_t* vals, int n)
 {
-	int off;
-	off = snprintf(sql_buf, SQL_BUF_LEN, "insert into %s (", CON_TABLE(_h));
-	off += print_columns(sql_buf + off, SQL_BUF_LEN - off, _k, _n);
-	off += snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values (");
-	off += print_values(sql_buf + off, SQL_BUF_LEN - off, _v, _n);
-	*(sql_buf + off++) = ')';
-	*(sql_buf + off) = '\0';
-
-	if(begin_transaction(_h, sql_buf)) return(-1);
-	if (submit_query(_h, sql_buf) < 0) {
-		LOG(L_ERR, "db_insert(): Error while inserting\n");
-		return -2;
-	}
-	free_query(_h);
-	commit_transaction(_h);
-	return(0);
+	int ret;
+	char* delete;
+	struct pg_params* params;
+
+	if (!con) {
+		LOG(L_ERR, "postgres:db_insert: Invalid parameter value\n");
+		return -1;
+	}
+
+	params = 0;
+	delete = 0;
+
+        delete = print_delete(con, keys, ops, n);
+	if (!delete) goto err;
+
+	params = new_pg_params(n);
+	if (!params) goto err;
+	if (params_add(params, con, vals, n) < 0) goto err;
+
+	do {
+		ret = submit_query(0, con, delete, params);
+		if (ret < 0) goto err;                       /* Unknown error, bail out */
+		if (ret > 0) {                               /* Disconnected, try to reconnect */
+			if (reconnect(con) < 0) goto err;    /* Failed to reconnect */
+			else continue;                       /* Try one more time (ret is > 0) */
+		}
+	} while(ret != 0);
+	
+	free_pg_params(params);
+	pkg_free(delete);
+	return 0;
+
+ err:
+	if (params) free_pg_params(params);
+	if (delete) pkg_free(delete);
+	return -1;
 }
 
 
 /*
- * Delete a row from the specified table
- * _h: structure representing database connection
- * _k: key names
- * _o: operators
- * _v: values of the keys that must match
- * _n: number of key=value pairs
+ * Update some rows in the specified table
+ * con  : structure representing database connection
+ * keys : key names
+ * ops  : operators
+ * vals : values of the keys that must match
+ * ucols: updated columns
+ * uvals: updated values of the columns
+ * n    : number of key=value pairs
+ * un   : number of columns to update
  */
-int db_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n)
+int db_update(db_con_t* con, db_key_t* keys, db_op_t* ops, db_val_t* vals,
+	      db_key_t* ucols, db_val_t* uvals, int n, int un)
 {
-	int off;
-	off = snprintf(sql_buf, SQL_BUF_LEN, "delete from %s", CON_TABLE(_h));
-	if (_n) {
-		off += snprintf(sql_buf + off, SQL_BUF_LEN - off, " where ");
-		off += print_where(sql_buf + off, SQL_BUF_LEN - off, _k,
-			_o, _v, _n);
-	}
-	if(begin_transaction(_h, sql_buf)) return(-1);
-	if (submit_query(_h, sql_buf) < 0) {
-		LOG(L_ERR, "db_delete(): Error while deleting\n");
-		return -2;
-	}
-	free_query(_h);
-	commit_transaction(_h);
-	return(0);
+	int ret;
+	char* update;
+	struct pg_params* params;
+
+	if (!con || !ucols || !uvals || !un) {
+		LOG(L_ERR, "db_update: Invalid parameter value\n");
+		return -1;
+	}
+
+	params = 0;
+	update = 0;
+
+	update = print_update(con, ucols, keys, ops, un, n);
+	if (!update) goto err;
+
+	params = new_pg_params(n + un);
+	if (!params) goto err;
+	if (params_add(params, con, uvals, un) < 0) goto err;
+	if (params_add(params, con, vals, n) < 0) goto err;
+
+	do {
+		ret = submit_query(0, con, update, params);
+		if (ret < 0) goto err;                       /* Unknown error, bail out */
+		if (ret > 0) {                               /* Disconnected, try to reconnect */
+			if (reconnect(con) < 0) goto err;    /* Failed to reconnect */
+			else continue;                       /* Try one more time (ret is > 0) */
+		}
+	} while(ret != 0);
+
+	free_pg_params(params);
+	pkg_free(update);
+	return 0;
+
+ err:
+	if (params) free_pg_params(params);
+	if (update) pkg_free(update);
+	return -1;
 }
 
 
 /*
- * Update some rows in the specified table
- * _h: 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
+ * Release a result set from memory
  */
-int db_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v,
-	      db_key_t* _uk, db_val_t* _uv, int _n, int _un)
+int db_free_result(db_con_t* con, db_res_t* res)
 {
-	int off;
-	off = snprintf(sql_buf, SQL_BUF_LEN, "update %s set ", CON_TABLE(_h));
-	off += print_set(sql_buf + off, SQL_BUF_LEN - off, _uk, _uv, _un);
-	if (_n) {
-		off += snprintf(sql_buf + off, SQL_BUF_LEN - off, " where ");
-		off += print_where(sql_buf + off, SQL_BUF_LEN - off, _k,
-			_o, _v, _n);
-		*(sql_buf + off) = '\0';
-	}
-
-	if(begin_transaction(_h, sql_buf)) return(-1);
-	if (submit_query(_h, sql_buf) < 0) {
-		LOG(L_ERR, "db_update(): Error while updating\n");
-		return -2;
-	}
-	free_query(_h);
-	commit_transaction(_h);
-	return(0);
+	free_result(res);
+	return 0;
 }

+ 20 - 39
modules/db_postgres/dbase.h

@@ -1,11 +1,11 @@
 /*
  * $Id$
  *
- * POSTGRES module, portions of this code were templated using
- * the mysql module, thus it's similarity.
- *
+ * Postgres module core functions
  *
+ * Portions Copyright (C) 2001-2005 FhG FOKUS
  * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005 iptelorg GmbH
  *
  * This file is part of ser, a free SIP server.
  *
@@ -27,18 +27,12 @@
  * 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
+#ifndef _DBASE_H
+#define _DBASE_H
+
 
 #include "../../db/db_con.h"
 #include "../../db/db_res.h"
@@ -50,72 +44,59 @@
 /*
  * Initialize database connection
  */
-db_con_t* db_init(const char* _sqlurl);
+db_con_t* db_init(const char* uri);
 
-/*
- * Close a database connection
- */
-void db_close(db_con_t* _h);
 
 /*
- * Return result of previous query
- */
-int get_result(db_con_t* _h, db_res_t** _r);
-
-/*
- * create a new result anchored on parent memory
+ * Close a database connection
  */
-db_res_t* new_result_pg(char *parent);
+void db_close(db_con_t* con);
 
 
 /*
  * Free all memory allocated by get_result
  */
-int db_free_query(db_con_t* _h, db_res_t* _r);
+int db_free_result(db_con_t* con, db_res_t* res);
 
 
 /*
  * Do a query
  */
-int db_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc,
-	     db_key_t _o, db_res_t** _r);
+int db_query(db_con_t* con, db_key_t* keys, db_op_t* ops, db_val_t* vals, db_key_t* cols, int n, int nc,
+	     db_key_t order, db_res_t** res);
 
 
 /*
  * Raw SQL query
  */
-int db_raw_query(db_con_t* _h, char* _s, db_res_t** _r);
+int db_raw_query(db_con_t* con, char* query, db_res_t** res);
 
 
 /*
  * Insert a row into table
  */
-int db_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n);
+int db_insert(db_con_t* con, db_key_t* keys, db_val_t* vals, int n);
 
 
 /*
  * Delete a row from table
  */
-int db_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n);
+int db_delete(db_con_t* con, db_key_t* keys, db_op_t* ops, db_val_t* vals, int n);
 
 
 /*
  * Update a row in table
  */
-int db_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v,
-	      db_key_t* _uk, db_val_t* _uv, int _n, int _un);
+int db_update(db_con_t* con, db_key_t* keys, db_op_t* ops, db_val_t* vals,
+	      db_key_t* ucols, db_val_t* uvals, int n, int un);
+
 
 
 /*
  * Store name of table that will be used by
  * subsequent database functions
  */
-int use_table(db_con_t* _h, const char* _t);
-
-int val2str(db_val_t* _v, char* _s, int* _len);
-
-int free_result(db_res_t* _r);
+int use_table(db_con_t* con, const char* table);
 
-int convert_result(db_con_t* _h, db_res_t* _r);
 
-#endif /* DBASE_H */
+#endif /* _DBASE_H */

+ 202 - 0
modules/db_postgres/pg_con.c

@@ -0,0 +1,202 @@
+/* 
+ * $Id$
+ *
+ * Portions Copyright (C) 2001-2003  FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005 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
+ */
+
+#include "pg_con.h"
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../ut.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 used by the server to store timestamp data type
+ * The function returns 1 if the server stores timestamps as int8 and 0
+ * if it is stored as double
+ */
+int timestamp_format(PGconn* con)
+{
+	unsigned long long offset;
+	PGresult* res = 0;
+	char* val;
+
+	res = PQexecParams(con, "select timestamp '2000-01-01 00:00:00' + time '00:00:01'", 0, 0, 0, 0, 0, 1);	
+
+	if (PQfformat(res, 0) != 1) {
+		LOG(L_ERR, "postgres:timestamp_format: Binary format expected but server sent text\n");
+		goto err;
+	}
+
+	if (PQntuples(res) != 1) {
+		LOG(L_ERR, "postgres:timestamp_format: 1 column expected, %d received\n", PQntuples(res));
+		goto err;
+	}
+
+	if (PQnfields(res) != 1) {
+		LOG(L_ERR, "postgres:timestamp_format: 1 Row expected, %d received\n", PQnfields(res));
+		goto err;
+	}
+
+	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:int_timestamp_format: Server uses int8 format for timestamps.\n");
+		return 1;
+	} else {
+		DBG("postgres:int_timestamp_format: Server uses double format for timestamps.\n");
+		return 0;
+	}
+	
+ err:
+	PQclear(res);
+	return -1;
+}
+
+
+/*
+ * Create a new connection structure,
+ * open the Postgres connection and set reference count to 1
+ */
+struct pg_con* new_connection(struct db_id* id)
+{
+	struct pg_con* ptr;
+	char* port_str;
+	int ret;
+	
+	if (!id) {
+		LOG(L_ERR, "postgres:new_connection: Invalid parameter value\n");
+		return 0;
+	}
+	
+	ptr = (struct pg_con*)pkg_malloc(sizeof(struct pg_con));
+	if (!ptr) {
+		LOG(L_ERR, "postgres:new_connection: No memory left\n");
+		return 0;
+	}
+	memset(ptr, 0, sizeof(struct pg_con));
+	ptr->ref = 1;
+	
+	if (id->port > 0) {
+		port_str = int2str(id->port, 0);
+	} else {
+		port_str = NULL;
+	}
+
+	if (id->port) {
+		DBG("postgres: Opening connection to: %s://%s:%s@%s:%d/%s\n",
+		    ZSW(id->scheme),
+		    ZSW(id->username),
+		    ZSW(id->password),
+		    ZSW(id->host),
+		    id->port,
+		    ZSW(id->database)
+		    );
+	} else {
+		DBG("postgres: Opening connection to: %s://%s:%s@%s/%s\n",
+		    ZSW(id->scheme),
+		    ZSW(id->username),
+		    ZSW(id->password),
+		    ZSW(id->host),
+		    ZSW(id->database)
+		    );
+	}
+	
+	ptr->con = PQsetdbLogin(id->host, port_str,
+				NULL, NULL, id->database,
+				id->username, id->password);
+	
+	if (ptr->con == 0) {
+		LOG(L_ERR, "postgres:new_connection: PQsetdbLogin ran out of memory\n");
+		goto err;
+	}
+	
+	if (PQstatus(ptr->con) != CONNECTION_OK) {
+		LOG(L_ERR, "postgres:new_connection: %s\n",
+		    PQerrorMessage(ptr->con));
+		goto err;
+	}
+
+	     /* Override default notice processor */
+	PQsetNoticeProcessor(ptr->con, notice_processor, 0);
+
+	DBG("postgres:new_connection: Connected. Protocol version=%d, Server version=%d\n", 
+	    PQprotocolVersion(ptr->con),
+	    PQserverVersion(ptr->con));
+
+	ptr->timestamp = time(0);
+	ptr->id = id;
+
+	ret = timestamp_format(ptr->con);
+	if (ret == 1 || ret == -1) {
+		     /* Assume INT8 representation if detection fails */
+		ptr->flags |= PG_INT8_TIMESTAMP;
+	}
+
+	return ptr;
+
+ err:
+	if (ptr && ptr->con) PQfinish(ptr->con);
+	if (ptr) pkg_free(ptr);
+	return 0;
+}
+
+
+/*
+ * Close the connection and release memory
+ */
+void free_connection(struct pg_con* con)
+{
+	if (!con) return;
+	if (con->id) free_db_id(con->id);
+	if (con->con) PQfinish(con->con);
+	pkg_free(con);
+}

+ 74 - 0
modules/db_postgres/pg_con.h

@@ -0,0 +1,74 @@
+/* 
+ * $Id$
+ *
+ * Portions Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005 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
+
+#include "../../db/db_pool.h"
+#include "../../db/db_id.h"
+
+#include <time.h>
+#include <libpq-fe.h>
+
+enum pg_con_flags {
+	PG_INT8_TIMESTAMP = 1 << 0
+};
+
+struct pg_con {
+	struct db_id* id;        /* Connection identifier */
+	unsigned int ref;        /* Reference count */
+	struct pool_con* next;   /* Next connection in the pool */
+
+	PGconn* con;             /* Postgres connection handle */
+	unsigned int flags;      /* Flags (currently only binary data format) */
+	time_t timestamp;        /* Timestamp of last query */
+};
+
+
+/*
+ * Some convenience wrappers
+ */
+#define CON_CONNECTION(db_con) (((struct pg_con*)((db_con)->tail))->con)
+#define CON_FLAGS(db_con)      (((struct pg_con*)((db_con)->tail))->flags)
+#define CON_TIMESTAMP(db_con)  (((struct pg_con*)((db_con)->tail))->timestamp)
+
+
+/*
+ * Create a new connection structure,
+ * open the MySQL connection and set reference count to 1
+ */
+struct pg_con* new_connection(struct db_id* id);
+
+
+/*
+ * Close the connection and release memory
+ */
+void free_connection(struct pg_con* con);
+
+#endif /* _PG_CON_H */

+ 76 - 89
modules/db_postgres/pg_type.h

@@ -1,96 +1,83 @@
-/*
- * $Id$
+/*-------------------------------------------------------------------------
  *
- * POSTGRES module, portions of this code were templated using
- * the mysql module, thus it's similarity.
+ * pg_type.h
+ *	  definition of the system "type" relation (pg_type)
+ *	  along with the relation's initial contents.
  *
  *
- * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
- * This file is part of ser, a free SIP server.
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.158 2004/12/31 22:03:26 pgsql Exp $
  *
- * 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
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
+ * NOTES
+ *	  the genbki.sh script reads this file and generates .bki
+ *	  information from the DATA() statements.
  *
+ *-------------------------------------------------------------------------
  */
-#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
+#ifndef _PG_TYPE_H
+#define _PG_TYPE_H
+
+/* OIDS 1 - 99 */
+
+#define BOOLOID	         16  /* boolean, 'true'/'false' */
+#define BYTEAOID         17  /* variable-length string, binary values escaped" */
+#define CHAROID	         18  /* single character */
+#define NAMEOID	         19  /* 63-character type for storing system identifiers */
+#define INT8OID	         20  /* ~18 digit integer, 8-byte storage */
+#define INT2OID	         21  /* -32 thousand to 32 thousand, 2-byte storage */
+#define INT2VECTOROID    22  /* array of INDEX_MAX_KEYS int2 integers, used in system tables */
+#define INT4OID	         23  /* -2 billion to 2 billion integer, 4-byte storage */
+#define REGPROCOID       24  /* registered procedure */
+#define TEXTOID	         25  /* variable-length string, no limit specified */
+#define OIDOID	         26  /* object identifier(oid), maximum 4 billion */
+#define TIDOID	         27  /* (Block, offset), physical location of tuple */
+#define XIDOID           28  /* transaction id */
+#define CIDOID           29  /* command identifier type, sequence in transaction id */
+#define OIDVECTOROID     30  /* array of INDEX_MAX_KEYS oids, used in system tables */
+
+/* OIDS 700 - 799 */
+
+#define FLOAT4OID       700  /* single-precision floating point number, 4-byte storage */
+#define FLOAT8OID       701  /* double-precision floating point number, 8-byte storage */
+#define ABSTIMEOID      702  /* absolute, limited-range date and time (Unix system time) */
+#define RELTIMEOID      703  /* relative, limited-range time interval (Unix delta time) */
+#define TINTERVALOID    704  /* (abstime,abstime), time interval */
+#define UNKNOWNOID      705
+#define CIRCLEOID       718  /* geometric circle '(center,radius)' */
+#define CASHOID         790  /* monetary amounts, $d,ddd.cc */
+
+/* OIDS 800 - 899 */
+
+#define MACADDROID      829  /* XX:XX:XX:XX:XX:XX, MAC address */
+#define INETOID         869  /* IP address/netmask, host address, netmask optional */
+#define CIDROID         650  /* network IP address/netmask, network address */
+
+/* OIDS 1000 - 1099 */
+
+#define BPCHAROID      1042  /* char(length), blank-padded string, fixed storage length */
+#define VARCHAROID     1043  /* varchar(length), non-blank-padded string, variable storage length */
+#define DATEOID	       1082  /* ANSI SQL date */
+#define TIMEOID	       1083  /* hh:mm:ss, ANSI SQL time */
+
+/* OIDS 1100 - 1199 */
+
+#define TIMESTAMPOID   1114  /* date and time */
+#define TIMESTAMPTZOID 1184  /* date and time with time zone */
+#define INTERVALOID    1186  /* @ <number> <units>, time interval */
+
+/* OIDS 1200 - 1299 */
+
+#define TIMETZOID      1266  /* hh:mm:ss, ANSI SQL time */
+
+/* OIDS 1500 - 1599 */
+
+#define BITOID	       1560  /* fixed-length bit string */
+#define VARBITOID      1562  /* variable-length bit string */
+
+/* OIDS 1700 - 1799 */
+
+#define NUMERICOID     1700  /* numeric(precision, decimal), arbitrary precision number */
+
+#endif /* _PG_TYPE_H */

+ 409 - 0
modules/db_postgres/res.c

@@ -0,0 +1,409 @@
+/* 
+ * $Id$ 
+ *
+ * Postgres module result related functions
+ *
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2005 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
+ */
+
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "pg_type.h"
+#include "pg_con.h"
+#include "db_mod.h"
+#include "res.h"
+#include <netinet/in.h>
+#include <string.h>
+
+
+/*
+ * Get and convert columns from a result
+ */
+static inline int get_columns(db_res_t* res)
+{
+	PGresult* pgres;
+	int n, i, type;
+
+	if (!res) {
+		LOG(L_ERR, "postgres:get_columns: Invalid parameter\n");
+		goto err;
+	}
+
+	pgres = (PGresult*)res->data;
+	if (!pgres) {
+		LOG(L_ERR, "postgres:get_columns: No postgres result found\n");
+		goto err;
+	}
+
+	n = PQnfields(pgres);
+	if (!n) {
+		LOG(L_ERR, "postgres:get_columns: No columns\n");
+		goto err;
+	}
+
+	if (n == 0) return 0;
+
+        res->col.names = (db_key_t*)pkg_malloc(sizeof(db_key_t) * n);
+	if (!res->col.names) {
+		LOG(L_ERR, "postgres:get_columns: No memory left\n");
+		goto err;
+	}
+
+	res->col.types = (db_type_t*)pkg_malloc(sizeof(db_type_t) * n);
+	if (!res->col.types) {
+		LOG(L_ERR, "postgres:get_columns: No memory left\n");
+		goto err;
+	}
+
+	res->col.n = n;
+
+	for(i = 0; i < n; i++) {
+		if (PQfformat(pgres, i) == 0) {
+			LOG(L_ERR, "postgres:get_column: Text format of columns not supported\n");
+			goto err;
+		}
+
+		res->col.names[i] = PQfname(pgres, i);
+		type = PQftype(pgres, i);
+		switch(type) {
+		case BOOLOID:    /* boolean, 'true'/'false' */
+		case INT2OID:    /* -32 thousand to 32 thousand, 2-byte storage */
+		case INT4OID:    /* -2 billion to 2 billion integer, 4-byte storage */
+		case INT8OID:    /* ~18 digit integer, 8-byte storage */
+			res->col.types[i] = DB_INT;
+			break;
+
+		case FLOAT4OID:  /* single-precision floating point number, 4-byte storage */
+		case FLOAT8OID:  /* double-precision floating point number, 8-byte storage */
+			res->col.types[i] = DB_DOUBLE;
+			break;
+
+		case TIMESTAMPOID:   /* date and time */
+		case TIMESTAMPTZOID: /* date and time with time zone */
+			res->col.types[i] = DB_DATETIME;
+			break;
+
+		case CHAROID:    /* single character */
+		case TEXTOID:    /* variable-length string, no limit specified */
+		case BPCHAROID:  /* char(length), blank-padded string, fixed storage length */
+		case VARCHAROID: /* varchar(length), non-blank-padded string, variable storage length */
+			res->col.types[i] = DB_STRING;
+			break;
+
+		case BYTEAOID: /* variable-length string, binary values escaped" */
+			res->col.types[i] = DB_BLOB;
+			break;
+
+		case BITOID:    /* fixed-length bit string */
+		case VARBITOID: /* variable-length bit string */
+			res->col.types[i] = DB_BITMAP;
+			break;
+
+		default:
+			LOG(L_ERR, "postgres:get_columns: Unsupported column type with oid %d\n", type);
+			goto err;
+		}
+	}
+	return 0;
+
+ err:
+	if (res->col.types) pkg_free(res->col.types);
+	if (res->col.names) pkg_free(res->col.names);
+	res->col.types = 0;
+	res->col.names = 0;
+	return -1;
+}
+
+
+/*
+ * Release memory used by columns
+ */
+static inline int free_columns(db_res_t* res)
+{
+	if (!res) {
+		LOG(L_ERR, "postgres:free_columns: Invalid parameter\n");
+		return -1;
+	}
+
+	if (res->col.names) pkg_free(res->col.names);
+	if (res->col.types) pkg_free(res->col.types);
+	return 0;
+}
+
+
+/*
+ * Release memory used by rows
+ */
+static inline void free_rows(db_res_t* res)
+{
+	int r;
+
+	if (!res->rows) return;
+
+	for(r = 0; r < res->n; r++) {
+		pkg_free(res->rows[r].values);
+	}
+	
+	pkg_free(res->rows);
+}
+
+
+static inline int convert_cell(db_con_t* con, db_res_t* res, int row, int col)
+{
+	static str dummy_str = {"", 0};
+	PGresult* pgres;
+	db_val_t* val;
+	int type, pglen;
+	const char* pgval;
+	union {
+		int i4;
+		long long i8;
+		float f4;
+		double f8;
+		char c[8];
+	} tmp;
+
+	val = &res->rows[row].values[col];
+	pgres = (PGresult*)res->data;
+
+	val->type = res->col.types[col];
+
+	if (PQgetisnull(pgres, row, col)) {
+		val->nul = 1;
+		switch(res->col.types[col]) {
+		case DB_INT:      val->val.int_val = 0;              break;
+		case DB_DOUBLE:   val->val.double_val = 0;           break;
+		case DB_STRING:   val->val.string_val = dummy_str.s; break;
+		case DB_STR:      val->val.str_val = dummy_str;      break;
+		case DB_DATETIME: val->val.time_val = 0;             break;
+		case DB_BLOB:     val->val.blob_val = dummy_str;     break;
+		case DB_BITMAP:   val->val.bitmap_val = 0;           break;
+		}
+		return 0;
+	}
+
+	val->nul = 0;
+	type = PQftype(pgres, col);
+	pgval = PQgetvalue(pgres, row, col);
+	pglen = PQgetlength(pgres, row, col);
+	
+	     /* Postgres delivers binary parameters in network byte order,
+	      * thus we have to convert them to host byte order. All data 
+	      * returned by PQgetvalue is zero terminated. Memory allocator
+	      * in libpq aligns data in memory properly so reading multibyte
+	      * values from memory at once is safe.
+	      */
+	switch(type) {
+	case BOOLOID: 
+		val->val.int_val = *pgval; 
+		break;
+
+	case INT2OID: 
+		val->val.int_val = ntohs(*(unsigned short*)pgval);
+		break;
+
+	case FLOAT4OID:
+		     /* FLOAT4 will be stored in (8-byte) double */
+		     /* FIXME: More efficient implementation could be done here
+		      * provided that we know that the numbers are stored in IEEE 754
+		      */
+		tmp.i4 = ntohl(*(unsigned int*)pgval);
+		val->val.double_val = tmp.f4;
+		break;
+
+	case INT4OID: 
+		val->val.int_val = ntohl(*(unsigned int*)pgval);
+		break;
+		
+	case INT8OID: 
+		val->val.int_val = ntohl(*(unsigned int*)(pgval + 4));
+		break; 
+
+	case FLOAT8OID:
+		tmp.i8 = (((unsigned long long)ntohl(*(unsigned int*)pgval)) << 32) + 
+			(unsigned int)ntohl(*(unsigned int*)(pgval + 4));
+		val->val.double_val = tmp.f8;
+		break;
+
+	case TIMESTAMPOID:
+	case TIMESTAMPTZOID:
+		tmp.i8 = (((unsigned long long)ntohl(*(unsigned int*)pgval)) << 32) + 
+			(unsigned int)ntohl(*(unsigned int*)(pgval + 4));
+		if (CON_FLAGS(con) & PG_INT8_TIMESTAMP) {
+			     /* int8 format */
+			val->val.time_val = tmp.i8 / 1000000 + PG_EPOCH_TIME;
+		} else {
+			     /* double format */
+			val->val.time_val = PG_EPOCH_TIME + (long long)tmp.f8;
+		}
+		break;
+		
+	case CHAROID:    /* single character */
+	case TEXTOID:    /* variable-length string, no limit specified */
+	case BPCHAROID:  /* char(length), blank-padded string, fixed storage length */
+	case VARCHAROID: /* varchar(length), non-blank-padded string, variable storage length */
+		val->val.str_val.s = (char*)pgval;
+		val->val.str_val.len = pglen;
+		break;
+		
+	case BYTEAOID: /* variable-length string, binary values escaped" */
+		val->val.blob_val.s = (char*)pgval;
+		val->val.blob_val.len = pglen;
+		break;
+		
+	case BITOID:    /* fixed-length bit string */
+	case VARBITOID: /* variable-length bit string */
+		if (ntohl(*(unsigned int*)pgval) != 32) {
+			LOG(L_ERR, "postgres:convert_cell: Only 32-bit long bitfieds supported\n");
+			return -1;
+		}
+		val->val.bitmap_val = ntohl(*(unsigned int*)(pgval + 4));
+		break;
+		
+	default:
+		LOG(L_ERR, "postgres:convert_cell: Unsupported column type with oid %d\n", type);
+		return -1;
+		
+	}
+
+	return 0;
+}
+ 
+ 
+/*
+ * Convert rows from postgres to db API representation
+ */
+static inline int convert_rows(db_con_t* con, db_res_t* res)
+{
+	db_row_t* row;
+	int r, c;
+
+	if (!res) {
+		LOG(L_ERR, "postgres:convert_rows: Invalid parameter\n");
+		return -1;
+	}
+
+	res->n = PQntuples((PGresult*)res->data); /* Number of rows */
+
+	     /* Assert: number of columns is > 0, otherwise get_columns would fail */
+	if (!res->n) {
+		res->rows = 0;
+		return 0;
+	}
+
+	res->rows = (struct db_row*)pkg_malloc(sizeof(db_row_t) * res->n);
+	if (!res->rows) {
+		LOG(L_ERR, "postgres:convert_rows: No memory left\n");
+		goto err;
+	}
+
+	for(r = 0; r < res->n; r++) {
+		row = &res->rows[r];
+		row->values = (db_val_t*)pkg_malloc(sizeof(db_val_t) * res->col.n);
+		if (!row->values) {
+			LOG(L_ERR, "postgres:convert_rows: No memory left to allocate row\n");
+			res->n = r; /* This is to make sure that the cleanup function release only rows
+				     * that has been really allocated
+				     */
+			goto err;
+		}
+		row->n = res->col.n;
+
+		for(c = 0; c < row->n; c++) {
+			if (convert_cell(con, res, r, c) < 0) {
+				row->n = c;
+				res->n = r;
+				goto err;
+			}
+		}
+	}
+
+	return 0;
+
+ err:
+	free_rows(res);
+	return -1;
+}
+
+
+/*
+ * Create a new result structure and initialize it
+ */
+db_res_t* new_result(PGresult* pgres)
+{
+	db_res_t* r;
+	r = (db_res_t*)pkg_malloc(sizeof(db_res_t));
+	if (!r) {
+		LOG(L_ERR, "postgres:new_result: No memory left\n");
+		return 0;
+	}
+
+	memset(r, 0, sizeof(db_res_t));
+	r->data = pgres;
+	return r;
+}
+
+
+/*
+ * Fill the structure with data from database
+ */
+int convert_result(db_res_t* res, db_con_t* con)
+{
+	if (!res) {
+		LOG(L_ERR, "postgres:convert_result: Invalid parameter\n");
+		return -1;
+	}
+
+	if (get_columns(res) < 0) {
+		LOG(L_ERR, "postgres:convert_result: Error while getting column names\n");
+		return -2;
+	}
+
+	if (convert_rows(con, res) < 0) {
+		LOG(L_ERR, "postgres:convert_result: Error while converting rows\n");
+		free_columns(res);
+		return -3;
+	}
+	return 0;
+}
+
+
+/*
+ * Release memory used by a result structure
+ */
+int free_result(db_res_t* res)
+{
+	if (!res) {
+		LOG(L_ERR, "postgres:free_result: Invalid parameter\n");
+		return -1;
+	}
+
+	free_columns(res);
+	free_rows(res);
+	if (res->data) PQclear((PGresult*)res->data);
+	pkg_free(res);
+	return 0;
+}

+ 25 - 23
modules/db_postgres/db_utils.h → modules/db_postgres/res.h

@@ -1,11 +1,11 @@
-/*
- * $Id$
- *
- * POSTGRES module, portions of this code were templated using
- * the mysql module, thus it's similarity.
+/* 
+ * $Id$ 
  *
+ * Postgres module result related functions
  *
- * Copyright (C) 2003 August.Net Services, LLC
+ * Portions Copyright (C) 2001-2003 FhG FOKUS
+ * Copyright (C) 2003 August.Net Services,, LLC
+ * Portions Copyright (C) iptelorg GmbH
  *
  * This file is part of ser, a free SIP server.
  *
@@ -27,29 +27,31 @@
  * 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 _RES_H
+#define _RES_H
+
+#include <libpq-fe.h>
+#include "../../db/db_res.h"
+#include "../../db/db_con.h"
+
+/*
+ * Create a new result structure and initialize it
+ */
+db_res_t* new_result(PGresult* pgres);
 
-#ifndef DB_UTILS_H
-#define DB_UTILS_H
 
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE
-#endif
-#include <time.h>
+/*
+ * Fill the structure with data from database
+ */
+int convert_result(db_res_t* res, db_con_t* con);
 
 
 /*
- * SQL URL parser
+ * Release memory used by a result structure
  */
-int parse_sql_url(char* _url, char** _user, char** _pass, 
-		  char** _host, char** _port, char** _db);
+int free_result(db_res_t* res);
+
 
-#endif
+#endif /* _RES_H */