Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 28690 - postfix+pgsql with unix domain sockets support
Summary: postfix+pgsql with unix domain sockets support
Status: RESOLVED WONTFIX
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: [OLD] Server (show other bugs)
Hardware: All Linux
: High normal (vote)
Assignee: Net-Mail Packages
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2003-09-14 05:47 UTC by Alessandro Pisani
Modified: 2004-01-26 09:44 UTC (History)
1 user (show)

See Also:
Package list:
Runtime testing required: ---


Attachments
postfix pgsql support patch for sockets (postfix-pgsql-debian-20030813.patch.bz2,10.47 KB, application/octet-stream)
2003-09-14 06:01 UTC, Alessandro Pisani
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Alessandro Pisani 2003-09-14 05:47:11 UTC
Unfortunately postgresql support patch for Postfix doesn't support unix domain
sockets. using postgresql on sockets instead of ip address could give some
security improvements in some situations.
here i'm attaching a patch which I created rediffing postfix-2.0.13 (but it
works also on 14 and 15) plus the current postgresql patch against Debian's
postfix-2.0.14-1 as of 2003/08/13 (which includes sockets support).

HTH
alessandro


Reproducible: Always
Steps to Reproduce:
1.
2.
3.
Comment 1 Alessandro Pisani 2003-09-14 06:01:27 UTC
Created attachment 17689 [details]
postfix pgsql support patch for sockets
Comment 2 Alessandro Pisani 2003-09-14 06:04:25 UTC
bugzilla gives me errors trying to upload the attachment, so i'm adding it there. 
p.s.: the patches postfix (I used 2.0.15-r1 plus my patch) compiles successfully on gentoo 
1.4 
 
---patch start--- 
diff -urN save/README_FILES/PGSQL_README 
postfix-2.0.13/README_FILES/PGSQL_README 
--- save/README_FILES/PGSQL_README	2003-09-14 13:37:46.000000000 +0200 
+++ postfix-2.0.13/README_FILES/PGSQL_README	2003-04-15 
17:28:51.000000000 +0200 
@@ -1,25 +1,46 @@ 
-[Code contributed by Mathieu Arnold] 
+PostgreSQL map type for Postfix. Currently this code is maintained 
+by LaMont Jones, <lamont@hp.com>. 
  
-We've written code to add a pgsql map type.  It utilizes the pgsql  
-client library, which can be obtained from: 
+This implementation allows for multiple pgsql databases: you can 
+use one for a virtual table, one for an access table, and one for 
+an aliases table if you want. 
  
-		   http://www.postgresql.org/ 
+You can specify multiple servers for the same database, so that 
+Postfix can switch to a good database server if one goes bad. 
  
-In order to build postfix with pgsql map support, you will need to add  
--DHAS_PGSQL and -I for the directory containing the postgres headers, and  
-the libpq library (and libcrypt) to AUXLIBS, for example: 
+Performance of postfix with pgsql has not been thoroughly tested, 
+however, we have found it to be stable.  Busy mail servers using 
+pgsql maps will generate lots of concurrent pgsql clients, so the 
+pgsql server(s) should be run with this fact in mind.  Any further 
+performance information, in addition to any feedback is most welcome. 
+ 
+This is based upon code written by Scott Cotton and Joshua Marcus, 
+IC Group, Inc.  The PostgreSQL changes were done by Aaron Sethman 
+<androsyn@ratbox.org>.  Updates for Postfix 1.1.x and PostgreSQL 
+7.1+, and support for calling stored procedures were added by Philip 
+Warner (pjw@rhyme.com.au). 
+ 
+Building Postfix with PostgreSQL support 
+======================================== 
+ 
+To use pgsql with Postfix on Debian GNU/Linux, you must install 
+the postfix-pgsql package. 
  
-make -f Makefile.init makefiles \ 
-	'CCARGS=-DHAS_PGSQL -I/some/where/include/postgresql' \ 
-	'AUXLIBS=/some/where/lib/postgres/libpq.a -lcrypt' 
+In order to build Postfix with pgsql map support, you specify 
+-DHAS_PGSQL, the directory with the PostgreSQL header files, and 
+the location of the libpq library file. 
  
-then, just run 'make'. 
+For example: 
  
-Postfix installations which may benefit from using pgsql map types 
-include sites that have a need for instantaneous updates of 
-forwarding, and sites that may benefit from having mail exchangers 
-reference a networked database, possibly working in conjunction with a 
-customer database of sorts. 
+    make tidy 
+    make -f Makefile.init makefiles \ 
+            'CCARGS=-DHAS_PGSQL -I/usr/local/include/pgsql' \ 
+            'AUXLIBS=-L/usr/local/lib -lpq' 
+ 
+Then just run 'make'. 
+ 
+Configuring PostgreSQL lookup tables 
+==================================== 
  
 Once postfix is built with pgsql support, you can specify a map type 
 in main.cf like this: 
@@ -27,17 +48,17 @@ 
 alias_maps = pgsql:/etc/postfix/pgsql-aliases.cf 
  
 The file /etc/postfix/pgsql-aliases.cf specifies lots of information 
-telling postfix how to reference the postgresql database.  An example 
-postgresql map config file follows: 
+telling postfix how to reference the pgsql database.  An example 
+pgsql map config file follows: 
  
 # 
-# postgresql config file for alias lookups on postfix 
+# pgsql config file for alias lookups on postfix 
 # comments are ok. 
 # 
  
 # the user name and password to log into the pgsql server 
 user = someone 
-password = some_passwordd  
+password = some_password  
  
 # the database name on the servers 
 dbname = customer_database 
@@ -45,44 +66,58 @@ 
 # the table name 
 table = mxaliases 
  
-# 
+# these should be obvious :-) 
 select_field = forw_addr 
 where_field = alias 
  
 # you may specify additional_conditions here 
 additional_conditions = and status = 'paid' 
  
-# the above variables will result in a query of 
-# the form:  
+# the above variables will result in a query of the form:  
+# 
 # select forw_addr from mxaliases where alias = '$lookup' and status = 'paid' 
+# 
 # ($lookup is escaped so if it contains single quotes or other odd 
 # characters, it will not cause a parse error in the sql). 
+ 
+# If you just want to use a PostgreSQL function, you can ignore the 
+# table name, select_field, where_field and additional_conditions, 
+# and just specify a database function to call: 
+ 
+#select_function = my_lookup_user_alias 
+ 
+# this will result in "select my_lookup_user_alias('name')" being 
+# used as the SQL statement to execute. If select_function is specified 
+# the table-related fields above will be ignored. 
+# 
+# As of 25-Jun-2002, if the function returns a single row and a single 
+# column AND that value is NULL, then the result will be treated as 
+# if the key was not in the dictionary. 
+# 
+# Future versions of PG will allow functions to return result sets. 
+# 
+ 
 # 
 # the hosts that postfix will try to connect to 
 # and query from (in the order listed) 
-hosts = host1.some.domain host2.some.domain 
- 
-# end postgresql config file 
+# specify unix: for unix-domain sockets, inet: for TCP connections (default) 
+hosts = host1.some.domain host2.some.domain unix:/file/name 
  
-Some notes: 
+# end pgsql config file 
  
-This configuration interface setup allows for multiple postgresql 
-databases: you can use one for a virtual table, one for an access 
-table, and one for an aliases table if you want. 
- 
-Since sites that have a need for multiple mail exchangers may enjoy 
-the convenience of using a networked mailer database, but do not want  
-to introduce a single point of failure to their system, we've included  
-the ability to have postfix reference multiple hosts for access to a 
-single pgsql map.  This will work if sites set up mirrored pgsql 
-databases on two or more hosts.  Whenever queries fail with an error 
-at one host, the rest of the hosts will be tried in order.  Each host 
-that is in an error state will undergo a reconnection attempt every so  
-often, and if no pgsql server hosts are reachable, then mail will be 
-deferred until atleast one of those hosts is reachable. 
+Using mirrored databases 
+======================== 
  
-Performance of postfix with pgsql has not been thoroughly tested, 
-however, we have found it to be stable.  Busy mail servers using pgsql 
-maps will generate lots of concurrent pgsql clients, so the pgsql 
-server(s) should be run with this fact in mind.  Any further 
-performance information, in addition to any feedback is most welcome. 
+Sites that have a need for multiple mail exchangers may enjoy the 
+convenience of using a networked mailer database, but do not want 
+to introduce a single point of failure to their system.   
+ 
+For this reason we've included the ability to have Postfix reference 
+multiple hosts for access to a single pgsql map.  This will work 
+if sites set up mirrored pgsql databases on two or more hosts. 
+ 
+Whenever queries fail with an error at one host, the rest of the 
+hosts will be tried in order.  Each host that is in an error state 
+will undergo a reconnection attempt every so often, and if no pgsql 
+server hosts are reachable, then mail will be deferred until at 
+least one of those hosts is reachable. 
diff -urN save/src/util/dict_pgsql.c postfix-2.0.13/src/util/dict_pgsql.c 
--- save/src/util/dict_pgsql.c	2003-09-14 13:37:46.000000000 +0200 
+++ postfix-2.0.13/src/util/dict_pgsql.c	2003-07-28 23:50:11.000000000 +0200 
@@ -1,45 +1,46 @@ 
- 
 /*++ 
 /* NAME 
 /*	dict_pgsql 3 
 /* SUMMARY 
-/*	dictionary manager interface to db files 
+/*	dictionary manager interface to Postgresql files 
 /* SYNOPSIS 
-/*	#include <dict.h> 
 /*	#include <dict_pgsql.h> 
 /* 
-/*	DICT	*dict_pgsql_open(name, dummy, unused_dict_flags) 
-/*	const char	*name; 
-/*	int     dummy; 
-/*	int     unused_dict_flags; 
+/*	DICT	*dict_pgsql_open(name, open_flags, dict_flags) 
+/*	const char *name; 
+/*	int     open_flags; 
+/*	int     dict_flags; 
 /* DESCRIPTION 
-/*	dict_pgsql_open() creates a dictionary of type 'pg'.  This 
+/*	dict_pgsql_open() creates a dictionary of type 'pgsql'.  This 
 /*	dictionary is an interface for the postfix key->value mappings 
 /*	to pgsql.  The result is a pointer to the installed dictionary, 
 /*	or a null pointer in case of problems. 
 /* 
-/*	The pgsql dictionary can manage multiple connections to different 
-/*	sql servers on different hosts.  It assumes that the underlying data 
-/*	on each host is identical (mirrored) and maintains one connection 
-/*	at any given time.  If any connection fails,  any other available 
-/*	ones will be opened and used.  The intent of this feature is to eliminate 
-/*	a single point of failure for mail systems that would otherwise rely 
-/*	on a single pgsql server. 
+/*	The pgsql dictionary can manage multiple connections to 
+/*	different sql servers for the same database.  It assumes that 
+/*	the underlying data on each server is identical (mirrored) and 
+/*	maintains one connection at any given time.  If any connection 
+/*	fails,  any other available ones will be opened and used. 
+/*	The intent of this feature is to eliminate a single point of 
+/*	failure for mail systems that would otherwise rely on a single 
+/*	pgsql server. 
 /* 
 /*	Arguments: 
 /* .IP name 
-/*	The path of the PostgreSQL configuration file.  The file encodes a number of 
-/*	pieces of information: username, password, databasename, table, 
-/*	select_field, where_field, and hosts.  For example, if you want the map to 
-/*	reference databases of the name "your_db" and execute a query like this: 
-/*	select forw_addr from aliases where alias like '<some username>' against 
-/*	any database called "vmailer_info" located on hosts host1.some.domain and 
-/*	host2.some.domain, logging in as user "vmailer" and password "passwd" then 
-/*	the configuration file should read: 
+/*	The path of the PostgreSQL configuration file.  The file 
+/*	encodes number of pieces of information: username, password, 
+/*	databasename, table, select_field, where_field, and hosts. 
+/*	For example, if you want the map to reference databases of 
+/*	the name "your_db" and execute a query like this:  select 
+/*	forw_addr from aliases where alias like '<some username>' 
+/*	against any database called "postfix_info" located on hosts 
+/*	host1.some.domain and host2.some.domain, logging in as user 
+/*	"postfix" and password "passwd" then the configuration file 
+/*	should read: 
 /* 
-/*	user = vmailer 
+/*	user = postfix 
 /*	password = passwd 
-/*	DBname = vmailer_info 
+/*	DBname = postfix_info 
 /*	table = aliases 
 /*	select_field = forw_addr 
 /*	where_field = alias 
@@ -47,17 +48,18 @@ 
 /* 
 /* .IP other_name 
 /*	reference for outside use. 
-/* .IP unusued_flags 
-/*	unused flags 
+/* .IP open_flags 
+/*	Must be O_RDONLY. 
+/* .IP dict_flags 
+/*	See dict_open(3). 
 /* SEE ALSO 
 /*	dict(3) generic dictionary manager 
 /* AUTHOR(S) 
-/*      Mathieu Arnold 
-/*      Absolight 
-/*      mat@absolight.com 
-/*       
-/*      based on dict_mysql by 
-/*       
+/*	Aaron Sethman 
+/*	androsyn@ratbox.org 
+/* 
+/*	Based upon dict_mysql.c by 
+/* 
 /*	Scott Cotton 
 /*	IC Group, Inc. 
 /*	scott@icgroup.com 
@@ -80,6 +82,8 @@ 
 #include <stdlib.h> 
 #include <syslog.h> 
 #include <time.h> 
+ 
+#include <postgres_ext.h> 
 #include <libpq-fe.h> 
  
 /* Utility library. */ 
@@ -92,29 +96,30 @@ 
 #include "split_at.h" 
 #include "find_inet.h" 
  
-/* need some structs to help organize things */ 
-typedef struct 
-{ 
+#define STATACTIVE	0 
+#define STATFAIL	1 
+#define STATUNTRIED	2 
+#define RETRY_CONN_INTV	60		/* 1 minute */ 
+ 
+typedef struct { 
     PGconn *db; 
     char   *hostname; 
     int     stat;			/* STATUNTRIED | STATFAIL | STATCUR */ 
-    time_t  ts;				/* used for attempting reconnection 
-					 * every so often if a host is down */ 
+    time_t  ts;				/* used for attempting reconnection */ 
 } HOST; 
  
-typedef struct 
-{ 
+typedef struct { 
     int     len_hosts;			/* number of hosts */ 
-    HOST   *db_hosts;			/* the hosts on which the databases 
-					 * reside */ 
+    HOST   *db_hosts;			/* hosts on which databases reside */ 
 } PLPGSQL; 
  
-typedef struct 
-{ 
+typedef struct { 
     char   *username; 
     char   *password; 
     char   *dbname; 
     char   *table; 
+    char   *query;			/* if set, overrides fields, etc */ 
+    char   *select_function; 
     char   *select_field; 
     char   *where_field; 
     char   *additional_conditions; 
@@ -122,45 +127,132 @@ 
     int     len_hosts; 
 } PGSQL_NAME; 
  
-typedef struct 
-{ 
+typedef struct { 
     DICT    dict; 
     PLPGSQL *pldb; 
     PGSQL_NAME *name; 
 } DICT_PGSQL; 
  
-#define STATACTIVE 0 
-#define STATFAIL 1 
-#define STATUNTRIED 2 
-#define RETRY_CONN_INTV 60			/* 1 minute */ 
  
-/* internal function declarations */ 
-static PLPGSQL *plpgsql_init (char *hostnames[], int); 
-static PGresult *plpgsql_query (PLPGSQL *, const char *, char *, char *, 
+/* Just makes things a little easier for me.. */ 
+#define PGSQL_RES PGresult 
  
-				char *); 
-static void plpgsql_dealloc (PLPGSQL *); 
-static void plpgsql_down_host (HOST *); 
-static void plpgsql_connect_single (HOST *, char *, char *, char *); 
-static int plpgsql_ready_reconn (HOST *); 
-static const char *dict_pgsql_lookup (DICT *, const char *); 
-DICT   *dict_pgsql_open (const char *, int, int); 
-static void dict_pgsql_close (DICT *); 
-static PGSQL_NAME *pgsqlname_parse (const char *); 
-static HOST host_init (char *); 
-void    pgsql_escape_string (char *escaped, const char *name, int len); 
+/* internal function declarations */ 
+static PLPGSQL *plpgsql_init(char *hostnames[], int); 
+static PGSQL_RES *plpgsql_query(PLPGSQL *, const char *, char *, char *, char *); 
+static void plpgsql_dealloc(PLPGSQL *); 
+static void plpgsql_close_host(HOST *); 
+static void plpgsql_down_host(HOST *); 
+static void plpgsql_connect_single(HOST *, char *, char *, char *); 
+static const char *dict_pgsql_lookup(DICT *, const char *); 
+DICT   *dict_pgsql_open(const char *, int, int); 
+static void dict_pgsql_close(DICT *); 
+static PGSQL_NAME *pgsqlname_parse(const char *); 
+static HOST host_init(char *); 
  
  
  
 /********************************************************************** 
  * public interface dict_pgsql_lookup 
  * find database entry return 0 if no alias found, set dict_errno 
- * on errors to DICT_ERRBO_RETRY and set dict_errno to 0 on success 
+ * on errors to DICT_ERROR_RETRY and set dict_errno to 0 on success 
  *********************************************************************/ 
-static const char *dict_pgsql_lookup (DICT *dict, const char *name) 
+static void pgsql_escape_string(char *new, const char *old, unsigned int len) 
+{ 
+    unsigned int x, 
+            y; 
+ 
+    /* 
+     * XXX We really should be using an escaper that is provided by the PGSQL 
+     * library. The code below seems to be over-kill (see RUS-CERT Advisory 
+     * 2001-08:01), but it's better to be safe than to be sorry -- Wietse 
+     */ 
+    for (x = 0, y = 0; x < len; x++, y++) { 
+	switch (old[x]) { 
+	case '\n': 
+	    new[y++] = '\\'; 
+	    new[y] = 'n'; 
+	    break; 
+	case '\r': 
+	    new[y++] = '\\'; 
+	    new[y] = 'r'; 
+	    break; 
+	case '\'': 
+	    new[y++] = '\\'; 
+	    new[y] = '\''; 
+	    break; 
+	case '"': 
+	    new[y++] = '\\'; 
+	    new[y] = '"'; 
+	    break; 
+	case 0: 
+	    new[y++] = '\\'; 
+	    new[y] = '0'; 
+	    break; 
+	default: 
+	    new[y] = old[x]; 
+	    break; 
+	} 
+    } 
+    new[y] = 0; 
+} 
+ 
+/* 
+ * expand a filter (lookup or result) 
+ */ 
+static void dict_pgsql_expand_filter(char *filter, char *value, VSTRING *out) 
+{ 
+    char   *myname = "dict_pgsql_expand_filter"; 
+    char   *sub, 
+           *end; 
+ 
+    /* 
+     * Yes, replace all instances of %s with the address to look up. Replace 
+     * %u with the user portion, and %d with the domain portion. 
+     */ 
+    sub = filter; 
+    end = sub + strlen(filter); 
+    while (sub < end) { 
+ 
+	/* 
+	 * Make sure it's %[sud] and not something else.  For backward 
+	 * compatibilty, treat anything other than %u or %d as %s, with a 
+	 * warning. 
+	 */ 
+	if (*(sub) == '%') { 
+	    char   *u = value; 
+	    char   *p = strrchr(u, '@'); 
+ 
+	    switch (*(sub + 1)) { 
+	    case 'd': 
+		if (p) 
+		    vstring_strcat(out, p + 1); 
+		break; 
+	    case 'u': 
+		if (p) 
+		    vstring_strncat(out, u, p - u); 
+		else 
+		    vstring_strcat(out, u); 
+		break; 
+	    default: 
+		msg_warn 
+		    ("%s: Invalid filter substitution format '%%%c'!", 
+		     myname, *(sub + 1)); 
+		break; 
+	    case 's': 
+		vstring_strcat(out, u); 
+		break; 
+	    } 
+	    sub++; 
+	} else 
+	    vstring_strncat(out, sub, 1); 
+	sub++; 
+    } 
+} 
+ 
+static const char *dict_pgsql_lookup(DICT *dict, const char *name) 
 { 
-    PGresult *query_res; 
-    char   *field; 
+    PGSQL_RES *query_res; 
     DICT_PGSQL *dict_pgsql; 
     PLPGSQL *pldb; 
     static VSTRING *result; 
@@ -169,154 +261,163 @@ 
             j, 
             numrows; 
     char   *name_escaped = 0; 
+    int     isFunctionCall; 
+    int     numcols; 
  
     dict_pgsql = (DICT_PGSQL *) dict; 
     pldb = dict_pgsql->pldb; 
     /* initialization  for query */ 
-    query = vstring_alloc (24); 
-    vstring_strcpy (query, ""); 
-    if ( 
-	(name_escaped = 
-	 (char *) mymalloc ((sizeof (char) * (strlen (name) * 2) + 1))) == 
-	NULL) 
-      { 
-	  msg_fatal ("dict_pgsql_lookup: out of memory."); 
-      } 
+    query = vstring_alloc(24); 
+    vstring_strcpy(query, ""); 
+    if ((name_escaped = (char *) mymalloc((sizeof(char) * (strlen(name) * 2) +1))) == 
NULL) { 
+	msg_fatal("dict_pgsql_lookup: out of memory."); 
+    } 
     /* prepare the query */ 
-    pgsql_escape_string (name_escaped, name, (unsigned int) strlen (name)); 
-    vstring_sprintf (query, "select %s from %s where %s = '%s' %s", 
-		     dict_pgsql->name->select_field, dict_pgsql->name->table, 
-		     dict_pgsql->name->where_field, name_escaped, 
-		     dict_pgsql->name->additional_conditions); 
+    pgsql_escape_string(name_escaped, name, (unsigned int) strlen(name)); 
+ 
+    /* Build SQL - either a select from table or select a function */ 
+ 
+    isFunctionCall = (dict_pgsql->name->select_function != NULL); 
+    if (isFunctionCall) { 
+	vstring_sprintf(query, "select %s('%s')", 
+			dict_pgsql->name->select_function, 
+			name_escaped); 
+    } else if (dict_pgsql->name->query) { 
+	dict_pgsql_expand_filter(dict_pgsql->name->query, name_escaped, query); 
+    } else { 
+	vstring_sprintf(query, "select %s from %s where %s = '%s' %s", 
dict_pgsql->name->select_field, 
+			dict_pgsql->name->table, 
+			dict_pgsql->name->where_field, 
+			name_escaped, 
+			dict_pgsql->name->additional_conditions); 
+    } 
+ 
     if (msg_verbose) 
-	msg_info ("dict_pgsql_lookup using sql query: %s", 
-		  vstring_str (query)); 
+	msg_info("dict_pgsql_lookup using sql query: %s", vstring_str(query)); 
+ 
     /* free mem associated with preparing the query */ 
-    myfree (name_escaped); 
+    myfree(name_escaped); 
+ 
     /* do the query - set dict_errno & cleanup if there's an error */ 
-    if ((query_res = plpgsql_query (pldb, 
-				    vstring_str (query), 
-				    dict_pgsql->name->dbname, 
-				    dict_pgsql->name->username, 
-				    dict_pgsql->name->password)) == 0) 
-      { 
-	  dict_errno = DICT_ERR_RETRY; 
-	  vstring_free (query); 
-	  return 0; 
-      } 
+    if ((query_res = plpgsql_query(pldb, 
+				   vstring_str(query), 
+				   dict_pgsql->name->dbname, 
+				   dict_pgsql->name->username, 
+				   dict_pgsql->name->password)) == 0) { 
+	dict_errno = DICT_ERR_RETRY; 
+	vstring_free(query); 
+	return 0; 
+    } 
     dict_errno = 0; 
     /* free the vstring query */ 
-    vstring_free (query); 
-    numrows = PQntuples (query_res); 
+    vstring_free(query); 
+    numrows = PQntuples(query_res); 
     if (msg_verbose) 
-	msg_info ("dict_pgsql_lookup: retrieved %d rows", numrows); 
-    if (numrows == 0) 
-      { 
-	  PQclear (query_res); 
-	  return 0; 
-      } 
+	msg_info("dict_pgsql_lookup: retrieved %d rows", numrows); 
+    if (numrows == 0) { 
+	PQclear(query_res); 
+	return 0; 
+    } 
+    numcols = PQnfields(query_res); 
+ 
+    if (numcols == 1 && numrows == 1 && isFunctionCall) { 
+ 
+	/* 
+	 * We do the above check because PostgreSQL 7.3 will allow functions 
+	 * to return result sets 
+	 */ 
+	if (PQgetisnull(query_res, 0, 0) == 1) { 
+ 
+	    /* 
+	     * Functions returning a single row & column that is null are 
+	     * deemed to have not found the key. 
+	     */ 
+	    PQclear(query_res); 
+	    return 0; 
+	} 
+    } 
     if (result == 0) 
-	result = vstring_alloc (10); 
-    vstring_strcpy (result, ""); 
-    for (i = 0; i < numrows; i++) 
-      { 
-	  if (i > 0) 
-	      vstring_strcat (result, ","); 
-	  for (j = 0; j < PQnfields (query_res); j++) 
-	    { 
-		if (j > 0) 
-		    vstring_strcat (result, ","); 
-		field = PQgetvalue (query_res, i, j); 
-		vstring_strcat (result, field); 
-		if (msg_verbose > 1) 
-		    msg_info ("dict_pgsql_lookup: retrieved field: %d: %s", j, 
-			      field); 
-	    } 
-      } 
-    PQclear (query_res); 
-    return vstring_str (result); 
+	result = vstring_alloc(10); 
+ 
+    vstring_strcpy(result, ""); 
+    for (i = 0; i < numrows; i++) { 
+	if (i > 0) 
+	    vstring_strcat(result, ","); 
+	for (j = 0; j < numcols; j++) { 
+	    if (j > 0) 
+		vstring_strcat(result, ","); 
+	    vstring_strcat(result, PQgetvalue(query_res, i, j)); 
+	    if (msg_verbose > 1) 
+		msg_info("dict_pgsql_lookup: retrieved field: %d: %s", j, 
PQgetvalue(query_res, i, j)); 
+	} 
+    } 
+    PQclear(query_res); 
+    return vstring_str(result); 
 } 
  
 /* 
- * plpgsql_query - process a PGSQL query.  Return PGresult* on success. 
+ * plpgsql_query - process a PostgreSQL query.  Return PGSQL_RES* on success. 
  *		     On failure, log failure and try other db instances. 
  *		     on failure of all db instances, return 0; 
  *		     close unnecessary active connections 
  */ 
  
-static PGresult *plpgsql_query (PLPGSQL * PLDB, 
-				const char *query, 
-				char *dbname, char *username, char *password) 
+static PGSQL_RES *plpgsql_query(PLPGSQL *PLDB, 
+				        const char *query, 
+				        char *dbname, 
+				        char *username, 
+				        char *password) 
 { 
     int     i; 
     HOST   *host; 
-    PGresult *res = 0; 
-    ExecStatusType status; 
+    PGSQL_RES *res = 0; 
  
-    for (i = 0; i < PLDB->len_hosts; i++) 
-      { 
-	  /* can't deal with typing or reading PLDB->db_hosts[i] over & over */ 
-	  host = &(PLDB->db_hosts[i]); 
-	  if (msg_verbose > 1) 
-	      msg_info ("dict_pgsql: trying host %s stat %d, last res %p", 
-			host->hostname, host->stat, res); 
- 
-	  /* answer already found */ 
-	  if (res != 0 && host->stat == STATACTIVE) 
-	    { 
-		if (msg_verbose) 
-		    msg_info 
-			("dict_pgsql: closing unnessary connection to %s", 
+    for (i = 0; i < PLDB->len_hosts; i++) { 
+	/* can't deal with typing or reading PLDB->db_hosts[i] over & over */ 
+	host = &(PLDB->db_hosts[i]); 
+	if (msg_verbose > 1) 
+	    msg_info("dict_pgsql: trying host %s stat %d, last res %p", host->hostname, 
host->stat, res); 
+ 
+	/* answer already found */ 
+	if (res != 0 && host->stat == STATACTIVE) { 
+	    if (msg_verbose) 
+		msg_info("dict_pgsql: closing unnessary connection to %s", 
 			 host->hostname); 
-		plpgsql_down_host (host); 
-	    } 
-	  /* try to connect for the first time if we don't have a result yet */ 
-	  if (res == 0 && host->stat == STATUNTRIED) 
-	    { 
-		if (msg_verbose) 
-		    msg_info ("dict_pgsql: attempting to connect to host %s", 
-			      host->hostname); 
-		plpgsql_connect_single (host, dbname, username, password); 
-	    } 
+	    plpgsql_close_host(host); 
+	} 
+	/* try to connect for the first time if we don't have a result yet */ 
+	if (res == 0 && host->stat == STATUNTRIED) { 
+	    if (msg_verbose) 
+		msg_info("dict_pgsql: attempting to connect to host %s", 
+			 host->hostname); 
+	    plpgsql_connect_single(host, dbname, username, password); 
+	} 
  
-	  /* 
-	   * try to reconnect if we don't have an answer and the host had a 
-	   * prob in the past and it's time for it to reconnect 
-	   */ 
-	  if (res == 0 && host->stat == STATFAIL 
-	      && host->ts < time ((time_t *) 0)) 
-	    { 
-		if (msg_verbose) 
-		    msg_info 
-			("dict_pgsql: attempting to reconnect to host %s", 
+	/* 
+	 * try to reconnect if we don't have an answer and the host had a 
+	 * prob in the past and it's time for it to reconnect 
+	 */ 
+	if (res == 0 && host->stat == STATFAIL && host->ts < time((time_t *) 0)) { 
+	    if (msg_verbose) 
+		msg_info("dict_pgsql: attempting to reconnect to host %s", 
 			 host->hostname); 
-		plpgsql_connect_single (host, dbname, username, password); 
-	    } 
+	    plpgsql_connect_single(host, dbname, username, password); 
+	} 
  
-	  /* 
-	   * if we don't have a result and the current host is marked active, 
-	   * try the query.  If the query fails, mark the host STATFAIL 
-	   */ 
-	  if (res == 0 && host->stat == STATACTIVE) 
-	    { 
-		res = PQexec (host->db, query); 
-		status = PQresultStatus (res); 
-		if (res 
-		    && (status = PGRES_COMMAND_OK 
-			|| status == PGRES_TUPLES_OK)) 
-		  { 
-		      if (msg_verbose) 
-			  msg_info 
-			      ("dict_pgsql: successful query from host %s", 
-			       host->hostname); 
-		  } 
-		else 
-		  { 
-		      msg_warn ("%s", PQerrorMessage (host->db)); 
-		      plpgsql_down_host (host); 
-		  } 
+	/* 
+	 * if we don't have a result and the current host is marked active, 
+	 * try the query.  If the query fails, mark the host STATFAIL 
+	 */ 
+	if (res == 0 && host->stat == STATACTIVE) { 
+	    if ((res = PQexec(host->db, query))) { 
+		if (msg_verbose) 
+		    msg_info("dict_pgsql: successful query from host %s", host->hostname); 
+	    } else { 
+		msg_warn("%s", PQerrorMessage(host->db)); 
+		plpgsql_down_host(host); 
 	    } 
-      } 
+	} 
+    } 
     return res; 
 } 
  
@@ -325,78 +426,63 @@ 
  * used to reconnect to a single database when one is down or none is 
  * connected yet. Log all errors and set the stat field of host accordingly 
  */ 
-static void 
-plpgsql_connect_single (HOST *host, char *dbname, char *username, 
-			char *password) 
+static void plpgsql_connect_single(HOST *host, char *dbname, char *username, char 
*password) 
 { 
     char   *destination = host->hostname; 
+    char   *unix_socket = 0; 
     char   *hostname = 0; 
     char   *service; 
-    VSTRING *conninfo = vstring_alloc (100); 
-    char   *conn; 
-    unsigned port = 0; 
+    char   *port = 0; 
  
     /* 
-     * Ad-hoc parsing code.  Expect "unix:pathname" or "inet:host:port", where 
+     * Ad-hoc parsing code. Expect "unix:pathname" or "inet:host:port", where 
      * both "inet:" and ":port" are optional. 
      */ 
-    if (strncmp (destination, "unix:", 5) == 0) 
-      { 
-	  vstring_sprintf_append (conninfo, "host=%s ", destination); 
-      } 
-    else 
-      { 
-	  if (strncmp (destination, "inet:", 5) == 0) 
-	      destination += 5; 
-	  hostname = mystrdup (destination); 
-	  if ((service = split_at (hostname, ':')) != 0) 
-	    { 
-		port = ntohs (find_inet_port (service, "tcp")); 
-		vstring_sprintf_append (conninfo, "host='%s' port='%d'", 
-					hostname, port); 
-	    } 
-	  else 
-	    { 
-		vstring_sprintf_append (conninfo, "host='%s'", hostname); 
-	    } 
-      } 
- 
-    vstring_sprintf_append (conninfo, " dbname='%s' user='%s' password='%s'", dbname, 
-			    username, password); 
-    conn = vstring_export (conninfo); 
- 
-    host->db = PQconnectdb (conn); 
- 
-    if ((host->db != NULL) && (PQstatus (host->db) == CONNECTION_OK)) 
-      { 
-	  if (msg_verbose) 
-	      msg_info ("dict_pgsql: successful connection to host %s", 
-			host->hostname); 
-	  host->stat = STATACTIVE; 
-      } 
-    else 
-      { 
-	  msg_warn ("%s", PQerrorMessage (host->db)); 
-	  plpgsql_down_host (host); 
-      } 
+    if (strncmp(destination, "unix:", 5) == 0) { 
+	unix_socket = destination + 5; 
+    } else { 
+	if (strncmp(destination, "inet:", 5) == 0) 
+	    destination += 5; 
+	hostname = mystrdup(destination); 
+	if ((service = split_at(hostname, ':')) != 0) 
+	    port = service; 
+    } 
+ 
+    if ((host->db = PQsetdbLogin(hostname, port, NULL, NULL, dbname, username, 
password))) { 
+	if (PQstatus(host->db) == CONNECTION_OK) { 
+	    if (msg_verbose) 
+		msg_info("dict_pgsql: successful connection to host %s", 
+			 host->hostname); 
+	    host->stat = STATACTIVE; 
+	} else 
+	    msg_warn("%s", PQerrorMessage(host->db)); 
+    } else { 
+	msg_warn("Unable to connect to database"); 
+	plpgsql_down_host(host); 
+    } 
     if (hostname) 
-	myfree (hostname); 
-    myfree (conn); 
+	myfree(hostname); 
+} 
+ 
+/* plpgsql_close_host - close an established PostgreSQL connection */ 
+ 
+static void plpgsql_close_host(HOST *host) 
+{ 
+    PQfinish(host->db); 
+    host->db = 0; 
+    host->stat = STATUNTRIED; 
 } 
  
 /* 
- * plpgsql_down_host - mark a HOST down update ts if marked down 
- * for the first time so that we'll know when to retry the connection 
+ * plpgsql_down_host - close a failed connection AND set a "stay away from 
+ * this host" timer. 
  */ 
-static void plpgsql_down_host (HOST *host) 
+static void plpgsql_down_host(HOST *host) 
 { 
-    if (host->stat != STATFAIL) 
-      { 
-	  host->ts = time ((time_t *) 0) + RETRY_CONN_INTV; 
-	  host->stat = STATFAIL; 
-      } 
-    PQfinish (host->db); 
+    PQfinish(host->db); 
     host->db = 0; 
+    host->ts = time((time_t *) 0) + RETRY_CONN_INTV; 
+    host->stat = STATFAIL; 
 } 
  
 /********************************************************************** 
@@ -405,33 +491,38 @@ 
  *    parse the map's config file 
  *    allocate memory 
  **********************************************************************/ 
-DICT   *dict_pgsql_open (const char *name, int unused_open_flags, 
-			 int dict_flags) 
+DICT   *dict_pgsql_open(const char *name, int open_flags, int dict_flags) 
 { 
     DICT_PGSQL *dict_pgsql; 
-    int     connections; 
  
-    dict_pgsql = (DICT_PGSQL *) dict_alloc (DICT_TYPE_PGSQL, name, 
-					    sizeof (DICT_PGSQL)); 
+    /* 
+     * Sanity checks. 
+     */ 
+    if (open_flags != O_RDONLY) 
+	msg_fatal("%s:%s map requires O_RDONLY access mode", 
+		  DICT_TYPE_PGSQL, name); 
+ 
+    dict_pgsql = (DICT_PGSQL *) dict_alloc(DICT_TYPE_PGSQL, name, 
+					   sizeof(DICT_PGSQL)); 
     dict_pgsql->dict.lookup = dict_pgsql_lookup; 
     dict_pgsql->dict.close = dict_pgsql_close; 
+    dict_pgsql->name = pgsqlname_parse(name); 
+    dict_pgsql->pldb = plpgsql_init(dict_pgsql->name->hostnames, 
+				    dict_pgsql->name->len_hosts); 
     dict_pgsql->dict.flags = dict_flags | DICT_FLAG_FIXED; 
-    dict_pgsql->name = pgsqlname_parse (name); 
-    dict_pgsql->pldb = plpgsql_init (dict_pgsql->name->hostnames, 
-				     dict_pgsql->name->len_hosts); 
     if (dict_pgsql->pldb == NULL) 
-	msg_fatal ("couldn't intialize pldb!\n"); 
-    dict_register (name, (DICT *) dict_pgsql); 
-    return (DICT_DEBUG (&dict_pgsql->dict)); 
+	msg_fatal("couldn't intialize pldb!\n"); 
+    dict_register(name, (DICT *) dict_pgsql); 
+    return &dict_pgsql->dict; 
 } 
  
 /* pgsqlname_parse - parse pgsql configuration file */ 
-static PGSQL_NAME *pgsqlname_parse (const char *pgsqlcf_path) 
+static PGSQL_NAME *pgsqlname_parse(const char *pgsqlcf_path) 
 { 
     int     i; 
     char   *nameval; 
     char   *hosts; 
-    PGSQL_NAME *name = (PGSQL_NAME *) mymalloc (sizeof 
(PGSQL_NAME)); 
+    PGSQL_NAME *name = (PGSQL_NAME *) mymalloc(sizeof(PGSQL_NAME)); 
     ARGV   *hosts_argv; 
     VSTRING *opt_dict_name; 
  
@@ -442,132 +533,125 @@ 
      * different paths, or the configuration info will cache across different 
      * pgsql maps, which can be confusing. 
      */ 
-    opt_dict_name = vstring_alloc (64); 
-    vstring_sprintf (opt_dict_name, "pgsql opt dict %s", pgsqlcf_path); 
-    dict_load_file (vstring_str (opt_dict_name), pgsqlcf_path); 
+    opt_dict_name = vstring_alloc(64); 
+    vstring_sprintf(opt_dict_name, "pgsql opt dict %s", pgsqlcf_path); 
+    dict_load_file(vstring_str(opt_dict_name), pgsqlcf_path); 
     /* pgsql username lookup */ 
-    if ( 
-	(nameval = 
-	 (char *) dict_lookup (vstring_str (opt_dict_name), "user")) == NULL) 
-	name->username = mystrdup (""); 
+    if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "user")) == 
NULL) 
+	name->username = mystrdup(""); 
     else 
-	name->username = mystrdup (nameval); 
+	name->username = mystrdup(nameval); 
     if (msg_verbose) 
-	msg_info ("pgsqlname_parse(): set username to '%s'", name->username); 
+	msg_info("pgsqlname_parse(): set username to '%s'", name->username); 
     /* password lookup */ 
-    if ( 
-	(nameval = 
-	 (char *) dict_lookup (vstring_str (opt_dict_name), 
-			       "password")) == NULL) 
-	name->password = mystrdup (""); 
+    if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "password")) == 
NULL) 
+	name->password = mystrdup(""); 
     else 
-	name->password = mystrdup (nameval); 
+	name->password = mystrdup(nameval); 
     if (msg_verbose) 
-	msg_info ("pgsqlname_parse(): set password to '%s'", name->password); 
+	msg_info("pgsqlname_parse(): set password to '%s'", name->password); 
  
     /* database name lookup */ 
-    if ( 
-	(nameval = 
-	 (char *) dict_lookup (vstring_str (opt_dict_name), 
-			       "dbname")) == NULL) 
- 
- 
- 
-	msg_fatal ("%s: pgsql options file does not include database name", 
-		   pgsqlcf_path); 
+    if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "dbname")) == 
NULL) 
+	msg_fatal("%s: pgsql options file does not include database name", pgsqlcf_path); 
     else 
-	name->dbname = mystrdup (nameval); 
+	name->dbname = mystrdup(nameval); 
     if (msg_verbose) 
-	msg_info ("pgsqlname_parse(): set database name to '%s'", 
-		  name->dbname); 
+	msg_info("pgsqlname_parse(): set database name to '%s'", name->dbname); 
  
     /* table lookup */ 
-    if ( 
-	(nameval = 
-	 (char *) dict_lookup (vstring_str (opt_dict_name), "table")) == NULL) 
-	msg_fatal ("%s: pgsql options file does not include table name", 
-		   pgsqlcf_path); 
+    if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "table")) == 
NULL) 
+	msg_fatal("%s: pgsql options file does not include table name", pgsqlcf_path); 
     else 
-	name->table = mystrdup (nameval); 
+	name->table = mystrdup(nameval); 
     if (msg_verbose) 
-	msg_info ("pgsqlname_parse(): set table name to '%s'", name->table); 
- 
-    /* select field lookup */ 
-    if ( 
-	(nameval = 
-	 (char *) dict_lookup (vstring_str (opt_dict_name), 
-			       "select_field")) == NULL) 
+	msg_info("pgsqlname_parse(): set table name to '%s'", name->table); 
  
+    name->select_function = NULL; 
+    name->query = NULL; 
  
+    /* 
+     * See what kind of lookup we have - a traditional 'select' or a function 
+     * call 
+     */ 
+    if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), 
"select_function")) != NULL) { 
  
-	msg_fatal ("%s: pgsql options file does not include select field", 
-		   pgsqlcf_path); 
-    else 
-	name->select_field = mystrdup (nameval); 
-    if (msg_verbose) 
-	msg_info ("pgsqlname_parse(): set select_field to '%s'", 
-		  name->select_field); 
- 
-    /* where field lookup */ 
-    if ( 
-	(nameval = 
-	 (char *) dict_lookup (vstring_str (opt_dict_name), 
-			       "where_field")) == NULL) 
-	msg_fatal ("%s: pgsql options file does not include where field", 
-		   pgsqlcf_path); 
-    else 
-	name->where_field = mystrdup (nameval); 
-    if (msg_verbose) 
-	msg_info ("pgsqlname_parse(): set where_field to '%s'", 
-		  name->where_field); 
- 
-    /* additional conditions */ 
-    if ( 
-	(nameval = 
-	 (char *) dict_lookup (vstring_str (opt_dict_name), 
-			       "additional_conditions")) == NULL) 
-	name->additional_conditions = mystrdup (""); 
-    else 
-	name->additional_conditions = mystrdup (nameval); 
-    if (msg_verbose) 
-	msg_info ("pgsqlname_parse(): set additional_conditions to '%s'", 
-		  name->additional_conditions); 
+	/* We have a 'select %s(%s)' function call. */ 
+	name->select_function = mystrdup(nameval); 
+	if (msg_verbose) 
+	    msg_info("pgsqlname_parse(): set function name to '%s'", name->table); 
+	/* query string */ 
+    } else if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "query")) != 
NULL) { 
+	name->query = mystrdup(nameval); 
+	if (msg_verbose) 
+	    msg_info("pgsqlname_parse(): set query to '%s'", name->query); 
+    } else { 
+ 
+	/* 
+	 * We have an old style 'select %s from %s...' call, so get the 
+	 * fields 
+	 */ 
+ 
+	/* table lookup */ 
+	if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "table")) == 
NULL) 
+	    msg_fatal("%s: pgsql options file does not include table name", pgsqlcf_path); 
+	else 
+	    name->table = mystrdup(nameval); 
+	if (msg_verbose) 
+	    msg_info("pgsqlname_parse(): set table name to '%s'", name->table); 
+ 
+	/* select field lookup */ 
+	if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "select_field")) 
== NULL) 
+	    msg_fatal("%s: pgsql options file does not include select field", pgsqlcf_path); 
+	else 
+	    name->select_field = mystrdup(nameval); 
+	if (msg_verbose) 
+	    msg_info("pgsqlname_parse(): set select_field to '%s'", name->select_field); 
+ 
+	/* where field lookup */ 
+	if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "where_field")) 
== NULL) 
+	    msg_fatal("%s: pgsql options file does not include where field", pgsqlcf_path); 
+	else 
+	    name->where_field = mystrdup(nameval); 
+	if (msg_verbose) 
+	    msg_info("pgsqlname_parse(): set where_field to '%s'", name->where_field); 
+ 
+	/* additional conditions */ 
+	if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), 
"additional_conditions")) == NULL) 
+	    name->additional_conditions = mystrdup(""); 
+	else 
+	    name->additional_conditions = mystrdup(nameval); 
+	if (msg_verbose) 
+	    msg_info("pgsqlname_parse(): set additional_conditions to '%s'", 
name->additional_conditions); 
+    } 
  
     /* pgsql server hosts */ 
-    if ( 
-	(nameval = 
-	 (char *) dict_lookup (vstring_str (opt_dict_name), "hosts")) == NULL) 
-	hosts = mystrdup (""); 
+    if ((nameval = (char *) dict_lookup(vstring_str(opt_dict_name), "hosts")) == 
NULL) 
+	hosts = mystrdup(""); 
     else 
-	hosts = mystrdup (nameval); 
+	hosts = mystrdup(nameval); 
     /* coo argv interface */ 
-    hosts_argv = argv_split (hosts, " ,\t\r\n"); 
+    hosts_argv = argv_split(hosts, " ,\t\r\n"); 
  
-    if (hosts_argv->argc == 0) 
-      {						/* no hosts specified, 
+    if (hosts_argv->argc == 0) {		/* no hosts specified, 
 						 * default to 'localhost' */ 
-	  if (msg_verbose) 
-	      msg_info 
-		  ("pgsqlname_parse(): no hostnames specified, defaulting to 'localhost'"); 
-	  argv_add (hosts_argv, "localhost", ARGV_END); 
-	  argv_terminate (hosts_argv); 
-      } 
+	if (msg_verbose) 
+	    msg_info("pgsqlname_parse(): no hostnames specified, defaulting to 
'localhost'"); 
+	argv_add(hosts_argv, "localhost", ARGV_END); 
+	argv_terminate(hosts_argv); 
+    } 
     name->len_hosts = hosts_argv->argc; 
-    name->hostnames = 
- 
-	(char **) mymalloc ((sizeof (char *)) * name->len_hosts); 
+    name->hostnames = (char **) mymalloc((sizeof(char *)) * name->len_hosts); 
     i = 0; 
-    for (i = 0; hosts_argv->argv[i] != NULL; i++) 
-      { 
-	  name->hostnames[i] = mystrdup (hosts_argv->argv[i]); 
-	  if (msg_verbose) 
-	      msg_info 
-		  ("pgsqlname_parse(): adding host '%s' to list of pgsql server hosts", 
-		   name->hostnames[i]); 
-      } 
-    myfree (hosts); 
-    vstring_free (opt_dict_name); 
-    argv_free (hosts_argv); 
+    for (i = 0; hosts_argv->argv[i] != NULL; i++) { 
+	name->hostnames[i] = mystrdup(hosts_argv->argv[i]); 
+	if (msg_verbose) 
+	    msg_info("pgsqlname_parse(): adding host '%s' to list of pgsql server hosts", 
+		     name->hostnames[i]); 
+    } 
+    myfree(hosts); 
+    vstring_free(opt_dict_name); 
+    argv_free(hosts_argv); 
     return name; 
 } 
  
@@ -576,36 +660,31 @@ 
  * plpgsql_init - initalize a PGSQL database. 
  *		    Return NULL on failure, or a PLPGSQL * on success. 
  */ 
-static PLPGSQL *plpgsql_init (char *hostnames[], int len_hosts) 
+static PLPGSQL *plpgsql_init(char *hostnames[], int len_hosts) 
 { 
     PLPGSQL *PLDB; 
     int     i; 
-    HOST    host; 
  
-    if ((PLDB = (PLPGSQL *) mymalloc (sizeof (PLPGSQL))) == NULL) 
-      { 
-	  msg_fatal ("mymalloc of pldb failed"); 
-      } 
+    if ((PLDB = (PLPGSQL *) mymalloc(sizeof(PLPGSQL))) == NULL) { 
+	msg_fatal("mymalloc of pldb failed"); 
+    } 
     PLDB->len_hosts = len_hosts; 
-    if ((PLDB->db_hosts = (HOST *) mymalloc (sizeof (HOST) * len_hosts)) 
-	== NULL) 
+    if ((PLDB->db_hosts = (HOST *) mymalloc(sizeof(HOST) * len_hosts)) == 
NULL) 
 	return NULL; 
- 
-    for (i = 0; i < len_hosts; i++) 
-      { 
-	  PLDB->db_hosts[i] = host_init (hostnames[i]); 
-      } 
+    for (i = 0; i < len_hosts; i++) { 
+	PLDB->db_hosts[i] = host_init(hostnames[i]); 
+    } 
     return PLDB; 
 } 
  
  
 /* host_init - initialize HOST structure */ 
-static HOST host_init (char *hostname) 
+static HOST host_init(char *hostname) 
 { 
     HOST    host; 
  
     host.stat = STATUNTRIED; 
-    host.hostname = mystrdup (hostname); 
+    host.hostname = mystrdup(hostname); 
     host.db = 0; 
     host.ts = 0; 
     return host; 
@@ -615,61 +694,39 @@ 
  * public interface dict_pgsql_close 
  * unregister, disassociate from database, freeing appropriate memory 
  **********************************************************************/ 
-static void dict_pgsql_close (DICT *dict) 
+static void dict_pgsql_close(DICT *dict) 
 { 
     int     i; 
     DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict; 
  
-    plpgsql_dealloc (dict_pgsql->pldb); 
-    myfree (dict_pgsql->name->username); 
-    myfree (dict_pgsql->name->password); 
-    myfree (dict_pgsql->name->dbname); 
-    myfree (dict_pgsql->name->table); 
-    myfree (dict_pgsql->name->select_field); 
-    myfree (dict_pgsql->name->where_field); 
-    myfree (dict_pgsql->name->additional_conditions); 
-    for (i = 0; i < dict_pgsql->name->len_hosts; i++) 
-      { 
-	  myfree (dict_pgsql->name->hostnames[i]); 
-      } 
-    myfree ((char *) dict_pgsql->name->hostnames); 
-    myfree ((char *) dict_pgsql->name); 
-    dict_free (dict); 
+    plpgsql_dealloc(dict_pgsql->pldb); 
+    myfree(dict_pgsql->name->username); 
+    myfree(dict_pgsql->name->password); 
+    myfree(dict_pgsql->name->dbname); 
+    myfree(dict_pgsql->name->table); 
+    myfree(dict_pgsql->name->select_field); 
+    myfree(dict_pgsql->name->where_field); 
+    myfree(dict_pgsql->name->additional_conditions); 
+    for (i = 0; i < dict_pgsql->name->len_hosts; i++) { 
+	myfree(dict_pgsql->name->hostnames[i]); 
+    } 
+    myfree((char *) dict_pgsql->name->hostnames); 
+    myfree((char *) dict_pgsql->name); 
+    dict_free(dict); 
 } 
  
 /* plpgsql_dealloc - free memory associated with PLPGSQL close databases */ 
-static void plpgsql_dealloc (PLPGSQL * PLDB) 
+static void plpgsql_dealloc(PLPGSQL *PLDB) 
 { 
     int     i; 
  
-    for (i = 0; i < PLDB->len_hosts; i++) 
-      { 
-	  if (PLDB->db_hosts[i].db) 
-	      PQfinish (PLDB->db_hosts[i].db); 
-	  myfree (PLDB->db_hosts[i].hostname); 
-      } 
-    myfree ((char *) PLDB->db_hosts); 
-    myfree ((char *) (PLDB)); 
-} 
- 
-/* pgsql_escape_string - replace mysql_escape_string */ 
-void    pgsql_escape_string (char *escaped, const char *name, int len) 
-{ 
-    int     i, 
-            j; 
- 
-    for (i = 0, j = 0; i <= len; i++, j++) 
-      { 
-	  if ((name[i] == '\'') || (name[i] == '\\')) 
-	    { 
-		escaped[j] = '\\'; 
-		j++; 
-	    } 
-	  escaped[j] = name[i]; 
-      } 
+    for (i = 0; i < PLDB->len_hosts; i++) { 
+	if (PLDB->db_hosts[i].db) 
+	    PQfinish(PLDB->db_hosts[i].db); 
+	myfree(PLDB->db_hosts[i].hostname); 
+    } 
+    myfree((char *) PLDB->db_hosts); 
+    myfree((char *) (PLDB)); 
 } 
  
- 
- 
 #endif 
- 
diff -urN save/src/util/dict_pgsql.h postfix-2.0.13/src/util/dict_pgsql.h 
--- save/src/util/dict_pgsql.h	2003-09-14 13:37:46.000000000 +0200 
+++ postfix-2.0.13/src/util/dict_pgsql.h	2003-04-14 19:31:46.000000000 +0200 
@@ -1,11 +1,11 @@ 
-#ifndef _DICT_PGSQL_H_INCLUDED_ 
-#define _DICT_PGSQL_H_INCLUDED_ 
+#ifndef _DICT_PGSQL_INCLUDED_ 
+#define _DICT_PGSQL_INCLUDED_ 
  
 /*++ 
 /* NAME 
 /*	dict_pgsql 3h 
 /* SUMMARY 
-/*	dictionary manager interface to pgsql databases 
+/*	dictionary manager interface to Postgresql files 
 /* SYNOPSIS 
 /*	#include <dict_pgsql.h> 
 /* DESCRIPTION 
@@ -19,15 +19,16 @@ 
  /* 
   * External interface. 
   */ 
-#define DICT_TYPE_PGSQL	"pgsql" 
+#define DICT_TYPE_PGSQL "pgsql" 
  
-extern DICT *dict_pgsql_open (const char *, int, int); 
+extern DICT *dict_pgsql_open(const char *name, int unused_flags, int dict_flags); 
  
-/* LICENSE 
-/* .ad 
-/* .fi 
-/*	The Secure Mailer license must be distributed with this software. 
 /* AUTHOR(S) 
+/*	Aaron Sethman 
+/*	androsyn@ratbox.org 
+/* 
+/*	Based upon dict_mysql.c by 
+/* 
 /*	Scott Cotton 
 /*	IC Group, Inc. 
 /*	scott@icgroup.com 
@@ -38,4 +39,3 @@ 
 /*--*/ 
  
 #endif 
- 
---patch end--- 
Comment 3 Alessandro Pisani 2003-09-19 14:47:50 UTC
i forgot: url for the debian files:
http://packages.debian.org/experimental/mail/postfix-pgsql.html
Comment 4 Max Kalika (RETIRED) gentoo-dev 2003-11-08 01:21:14 UTC
Have you tried configuring your postgresql map with a setting like the following?

   hosts = unix:/path/to/socket

This is supposed to work according to the code.  And I'm pretty sure all
the postgresql patches stem from the same core.  Please try it and report
back.
Comment 5 Alessandro Pisani 2003-11-08 05:39:00 UTC
Max: uhm...indeed it seems that the postfix-pgsql-2.0.0.2 patch already included
in the postfix ebuild support unix sokects despite my notice of the contrary.
Sorry about not having noticed I was wrong before.
bug can be safely closed.
Comment 6 Max Kalika (RETIRED) gentoo-dev 2003-11-08 09:39:09 UTC
Thank you for confirming.  Closing.
Comment 7 Alessandro Pisani 2003-11-16 11:54:19 UTC
calling for a REOPENing of this bug.
I just did a lot fo tests and the postfix-pgsql-2.0.0.2 patch and despite source code evidence, it doesn't support pgsql on unix sockets; infact, having it on a socket will result in errors from postfix, like:


Sep 19 17:09:04 farstar postfix/trivial-rewrite[1882]: warning: could not connect to server: No such file or directory??Is the server running locally and accepting??connections on Unix domain socket "/tmp/.s.PGSQL.5432"??
Sep 19 17:09:04 farstar postfix/trivial-rewrite[1882]: warning: could not connect to server: No such file or directory??Is the server running locally and accepting??connections on Unix domain socket "/tmp/.s.PGSQL.5432"??
Sep 19 17:09:04 farstar postfix/trivial-rewrite[1882]: fatal: pgsql:/etc/postfix/virtual.cf: table lookup problem

etc etc....
compiling postfix with the debian patch I previously posted in this bug solves the problem.
Comment 8 Alessandro Pisani 2003-11-16 12:21:21 UTC
news: it seems that postfix as of 2003-11-13 onw includes pgsql support.
so the debian patch is not needed anymore. reclose the bug: we can wait 2.0.17
and drop postfix-pgsql-2.0.0.2.patch in it.

alessandro
Comment 9 Max Kalika (RETIRED) gentoo-dev 2004-01-26 09:44:46 UTC
This will be supported in postfix 2.1 whenever that is out.  For now, we'll just use the current patch as-is.