Sync usage with man page.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / dict_pgsql.c
blobef0a953204ec836a61bc8029a89b3c3e6ccc2a45
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* dict_pgsql 3
6 /* SUMMARY
7 /* dictionary manager interface to PostgreSQL databases
8 /* SYNOPSIS
9 /* #include <dict_pgsql.h>
11 /* DICT *dict_pgsql_open(name, open_flags, dict_flags)
12 /* const char *name;
13 /* int open_flags;
14 /* int dict_flags;
15 /* DESCRIPTION
16 /* dict_pgsql_open() creates a dictionary of type 'pgsql'. This
17 /* dictionary is an interface for the postfix key->value mappings
18 /* to pgsql. The result is a pointer to the installed dictionary,
19 /* or a null pointer in case of problems.
21 /* The pgsql dictionary can manage multiple connections to
22 /* different sql servers for the same database. It assumes that
23 /* the underlying data on each server is identical (mirrored) and
24 /* maintains one connection at any given time. If any connection
25 /* fails, any other available ones will be opened and used.
26 /* The intent of this feature is to eliminate a single point of
27 /* failure for mail systems that would otherwise rely on a single
28 /* pgsql server.
29 /* .PP
30 /* Arguments:
31 /* .IP name
32 /* Either the path to the PostgreSQL configuration file (if it
33 /* starts with '/' or '.'), or the prefix which will be used to
34 /* obtain main.cf configuration parameters for this search.
36 /* In the first case, the configuration parameters below are
37 /* specified in the file as \fIname\fR=\fBvalue\fR pairs.
39 /* In the second case, the configuration parameters are
40 /* prefixed with the value of \fIname\fR and an underscore,
41 /* and they are specified in main.cf. For example, if this
42 /* value is \fIpgsqlsource\fR, the parameters would look like
43 /* \fIpgsqlsource_user\fR, \fIpgsqlsource_table\fR, and so on.
44 /* .IP other_name
45 /* reference for outside use.
46 /* .IP open_flags
47 /* Must be O_RDONLY.
48 /* .IP dict_flags
49 /* See dict_open(3).
51 /* .PP
52 /* Configuration parameters:
54 /* The parameters encode a number of pieces of information:
55 /* username, password, databasename, table, select_field,
56 /* where_field, and hosts:
57 /* .IP \fIuser\fR
58 /* Username for connecting to the database.
59 /* .IP \fIpassword\fR
60 /* Password for the above.
61 /* .IP \fIdbname\fR
62 /* Name of the database.
63 /* .IP \fIquery\fR
64 /* Query template. If not defined a default query template is constructed
65 /* from the legacy \fIselect_function\fR or failing that the \fItable\fR,
66 /* \fIselect_field\fR, \fIwhere_field\fR, and \fIadditional_conditions\fR
67 /* parameters. Before the query is issues, variable substitutions are
68 /* performed. See pgsql_table(5).
69 /* .IP \fIdomain\fR
70 /* List of domains the queries should be restricted to. If
71 /* specified, only FQDN addresses whose domain parts matching this
72 /* list will be queried against the SQL database. Lookups for
73 /* partial addresses are also supressed. This can significantly
74 /* reduce the query load on the server.
75 /* .IP \fIresult_format\fR
76 /* The format used to expand results from queries. Substitutions
77 /* are performed as described in pgsql_table(5). Defaults to returning
78 /* the lookup result unchanged.
79 /* .IP expansion_limit
80 /* Limit (if any) on the total number of lookup result values. Lookups which
81 /* exceed the limit fail with dict_errno=DICT_ERR_RETRY. Note that each
82 /* non-empty (and non-NULL) column of a multi-column result row counts as
83 /* one result.
84 /* .IP \fIselect_function\fR
85 /* When \fIquery\fR is not defined, the function to be used instead of
86 /* the default query based on the legacy \fItable\fR, \fIselect_field\fR,
87 /* \fIwhere_field\fR, and \fIadditional_conditions\fR parameters.
88 /* .IP \fItable\fR
89 /* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
90 /* FROM table used to construct the default query template, see pgsql_table(5).
91 /* .IP \fIselect_field\fR
92 /* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
93 /* SELECT field used to construct the default query template, see pgsql_table(5).
94 /* .IP \fIwhere_field\fR
95 /* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
96 /* WHERE field used to construct the default query template, see pgsql_table(5).
97 /* .IP \fIadditional_conditions\fR
98 /* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
99 /* additional text to add to the WHERE field in the default query template (this
100 /* usually begins with "and") see pgsql_table(5).
101 /* .IP \fIhosts\fR
102 /* List of hosts to connect to.
103 /* .PP
104 /* For example, if you want the map to reference databases of
105 /* the name "your_db" and execute a query like this: select
106 /* forw_addr from aliases where alias like '<some username>'
107 /* against any database called "postfix_info" located on hosts
108 /* host1.some.domain and host2.some.domain, logging in as user
109 /* "postfix" and password "passwd" then the configuration file
110 /* should read:
111 /* .PP
112 /* \fIuser\fR = \fBpostfix\fR
113 /* .br
114 /* \fIpassword\fR = \fBpasswd\fR
115 /* .br
116 /* \fIdbname\fR = \fBpostfix_info\fR
117 /* .br
118 /* \fItable\fR = \fBaliases\fR
119 /* .br
120 /* \fIselect_field\fR = \fBforw_addr\fR
121 /* .br
122 /* \fIwhere_field\fR = \fBalias\fR
123 /* .br
124 /* \fIhosts\fR = \fBhost1.some.domain\fR \fBhost2.some.domain\fR
125 /* .PP
126 /* SEE ALSO
127 /* dict(3) generic dictionary manager
128 /* AUTHOR(S)
129 /* Aaron Sethman
130 /* androsyn@ratbox.org
132 /* Based upon dict_mysql.c by
134 /* Scott Cotton
135 /* IC Group, Inc.
136 /* scott@icgroup.com
138 /* Joshua Marcus
139 /* IC Group, Inc.
140 /* josh@icgroup.com
141 /*--*/
143 /* System library. */
145 #include "sys_defs.h"
147 #ifdef HAS_PGSQL
148 #include <sys/socket.h>
149 #include <netinet/in.h>
150 #include <arpa/inet.h>
151 #include <netdb.h>
152 #include <stdio.h>
153 #include <string.h>
154 #include <stdlib.h>
155 #include <syslog.h>
156 #include <time.h>
158 #include <postgres_ext.h>
159 #include <libpq-fe.h>
161 /* Utility library. */
163 #include "dict.h"
164 #include "msg.h"
165 #include "mymalloc.h"
166 #include "argv.h"
167 #include "vstring.h"
168 #include "split_at.h"
169 #include "find_inet.h"
170 #include "myrand.h"
171 #include "events.h"
172 #include "stringops.h"
174 /* Global library. */
176 #include "cfg_parser.h"
177 #include "db_common.h"
179 /* Application-specific. */
181 #include "dict_pgsql.h"
183 #define STATACTIVE (1<<0)
184 #define STATFAIL (1<<1)
185 #define STATUNTRIED (1<<2)
187 #define TYPEUNIX (1<<0)
188 #define TYPEINET (1<<1)
190 #define RETRY_CONN_MAX 100
191 #define RETRY_CONN_INTV 60 /* 1 minute */
192 #define IDLE_CONN_INTV 60 /* 1 minute */
194 typedef struct {
195 PGconn *db;
196 char *hostname;
197 char *name;
198 char *port;
199 unsigned type; /* TYPEUNIX | TYPEINET */
200 unsigned stat; /* STATUNTRIED | STATFAIL | STATCUR */
201 time_t ts; /* used for attempting reconnection */
202 } HOST;
204 typedef struct {
205 int len_hosts; /* number of hosts */
206 HOST **db_hosts; /* hosts on which databases reside */
207 } PLPGSQL;
209 typedef struct {
210 DICT dict;
211 CFG_PARSER *parser;
212 char *query;
213 char *result_format;
214 void *ctx;
215 int expansion_limit;
216 char *username;
217 char *password;
218 char *dbname;
219 char *table;
220 ARGV *hosts;
221 PLPGSQL *pldb;
222 HOST *active_host;
223 } DICT_PGSQL;
226 /* Just makes things a little easier for me.. */
227 #define PGSQL_RES PGresult
229 /* internal function declarations */
230 static PLPGSQL *plpgsql_init(ARGV *);
231 static PGSQL_RES *plpgsql_query(DICT_PGSQL *, const char *, VSTRING *, char *,
232 char *, char *);
233 static void plpgsql_dealloc(PLPGSQL *);
234 static void plpgsql_close_host(HOST *);
235 static void plpgsql_down_host(HOST *);
236 static void plpgsql_connect_single(HOST *, char *, char *, char *);
237 static const char *dict_pgsql_lookup(DICT *, const char *);
238 DICT *dict_pgsql_open(const char *, int, int);
239 static void dict_pgsql_close(DICT *);
240 static HOST *host_init(const char *);
242 /* dict_pgsql_quote - escape SQL metacharacters in input string */
244 static void dict_pgsql_quote(DICT *dict, const char *name, VSTRING *result)
246 DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
247 HOST *active_host = dict_pgsql->active_host;
248 char *myname = "dict_pgsql_quote";
249 size_t len = strlen(name);
250 size_t buflen = 2*len + 1;
251 int err = 1;
253 if (active_host == 0)
254 msg_panic("%s: bogus dict_pgsql->active_host", myname);
257 * We won't get arithmetic overflows in 2*len + 1, because Postfix
258 * input keys have reasonable size limits, better safe than sorry.
260 if (buflen <= len)
261 msg_panic("%s: arithmetic overflow in 2*%lu+1",
262 myname, (unsigned long) len);
265 * XXX Workaround: stop further processing when PQescapeStringConn()
266 * (below) fails. A more proper fix requires invasive changes, not
267 * suitable for a stable release.
269 if (active_host->stat == STATFAIL)
270 return;
273 * Escape the input string, using PQescapeStringConn(), because
274 * the older PQescapeString() is not safe anymore, as stated by the
275 * documentation.
277 * From current libpq (8.1.4) documentation:
279 * PQescapeStringConn writes an escaped version of the from string
280 * to the to buffer, escaping special characters so that they cannot
281 * cause any harm, and adding a terminating zero byte.
283 * ...
285 * The parameter from points to the first character of the string
286 * that is to be escaped, and the length parameter gives the number
287 * of bytes in this string. A terminating zero byte is not required,
288 * and should not be counted in length.
290 * ...
292 * (The parameter) to shall point to a buffer that is able to hold
293 * at least one more byte than twice the value of length, otherwise
294 * the behavior is undefined.
296 * ...
298 * If the error parameter is not NULL, then *error is set to zero on
299 * success, nonzero on error ... The output string is still generated
300 * on error, but it can be expected that the server will reject it as
301 * malformed. On error, a suitable message is stored in the conn
302 * object, whether or not error is NULL.
304 VSTRING_SPACE(result, buflen);
305 PQescapeStringConn(active_host->db, vstring_end(result), name, len, &err);
306 if (err == 0) {
307 VSTRING_SKIP(result);
308 } else {
310 * PQescapeStringConn() failed. According to the docs, we still
311 * have a valid, null-terminated output string, but we need not
312 * rely on this behavior.
314 msg_warn("dict pgsql: (host %s) cannot escape input string: %s",
315 active_host->hostname, PQerrorMessage(active_host->db));
316 active_host->stat = STATFAIL;
317 VSTRING_TERMINATE(result);
321 /* dict_pgsql_lookup - find database entry */
323 static const char *dict_pgsql_lookup(DICT *dict, const char *name)
325 const char *myname = "dict_pgsql_lookup";
326 PGSQL_RES *query_res;
327 DICT_PGSQL *dict_pgsql;
328 PLPGSQL *pldb;
329 static VSTRING *query;
330 static VSTRING *result;
331 int i;
332 int j;
333 int numrows;
334 int numcols;
335 int expansion;
336 const char *r;
338 dict_pgsql = (DICT_PGSQL *) dict;
339 pldb = dict_pgsql->pldb;
341 #define INIT_VSTR(buf, len) do { \
342 if (buf == 0) \
343 buf = vstring_alloc(len); \
344 VSTRING_RESET(buf); \
345 VSTRING_TERMINATE(buf); \
346 } while (0)
348 INIT_VSTR(query, 10);
349 INIT_VSTR(result, 10);
351 dict_errno = 0;
354 * Optionally fold the key.
356 if (dict->flags & DICT_FLAG_FOLD_FIX) {
357 if (dict->fold_buf == 0)
358 dict->fold_buf = vstring_alloc(10);
359 vstring_strcpy(dict->fold_buf, name);
360 name = lowercase(vstring_str(dict->fold_buf));
364 * If there is a domain list for this map, then only search for
365 * addresses in domains on the list. This can significantly reduce
366 * the load on the server.
368 if (db_common_check_domain(dict_pgsql->ctx, name) == 0) {
369 if (msg_verbose)
370 msg_info("%s: Skipping lookup of '%s'", myname, name);
371 return (0);
375 * Suppress the actual lookup if the expansion is empty.
377 * This initial expansion is outside the context of any
378 * specific host connection, we just want to check the
379 * key pre-requisites, so when quoting happens separately
380 * for each connection, we don't bother with quoting...
382 if (!db_common_expand(dict_pgsql->ctx, dict_pgsql->query,
383 name, 0, query, 0))
384 return (0);
386 /* do the query - set dict_errno & cleanup if there's an error */
387 if ((query_res = plpgsql_query(dict_pgsql, name, query,
388 dict_pgsql->dbname,
389 dict_pgsql->username,
390 dict_pgsql->password)) == 0) {
391 dict_errno = DICT_ERR_RETRY;
392 return 0;
395 numrows = PQntuples(query_res);
396 if (msg_verbose)
397 msg_info("%s: retrieved %d rows", myname, numrows);
398 if (numrows == 0) {
399 PQclear(query_res);
400 return 0;
402 numcols = PQnfields(query_res);
404 for (expansion = i = 0; i < numrows && dict_errno == 0; i++) {
405 for (j = 0; j < numcols; j++) {
406 r = PQgetvalue(query_res, i, j);
407 if (db_common_expand(dict_pgsql->ctx, dict_pgsql->result_format,
408 r, name, result, 0)
409 && dict_pgsql->expansion_limit > 0
410 && ++expansion > dict_pgsql->expansion_limit) {
411 msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
412 myname, dict_pgsql->parser->name, name);
413 dict_errno = DICT_ERR_RETRY;
414 break;
418 PQclear(query_res);
419 r = vstring_str(result);
420 return ((dict_errno == 0 && *r) ? r : 0);
423 /* dict_pgsql_check_stat - check the status of a host */
425 static int dict_pgsql_check_stat(HOST *host, unsigned stat, unsigned type,
426 time_t t)
428 if ((host->stat & stat) && (!type || host->type & type)) {
429 /* try not to hammer the dead hosts too often */
430 if (host->stat == STATFAIL && host->ts > 0 && host->ts >= t)
431 return 0;
432 return 1;
434 return 0;
437 /* dict_pgsql_find_host - find a host with the given status */
439 static HOST *dict_pgsql_find_host(PLPGSQL *PLDB, unsigned stat, unsigned type)
441 time_t t;
442 int count = 0;
443 int idx;
444 int i;
446 t = time((time_t *) 0);
447 for (i = 0; i < PLDB->len_hosts; i++) {
448 if (dict_pgsql_check_stat(PLDB->db_hosts[i], stat, type, t))
449 count++;
452 if (count) {
453 idx = (count > 1) ?
454 1 + count * (double) myrand() / (1.0 + RAND_MAX) : 1;
456 for (i = 0; i < PLDB->len_hosts; i++) {
457 if (dict_pgsql_check_stat(PLDB->db_hosts[i], stat, type, t) &&
458 --idx == 0)
459 return PLDB->db_hosts[i];
462 return 0;
465 /* dict_pgsql_get_active - get an active connection */
467 static HOST *dict_pgsql_get_active(PLPGSQL *PLDB, char *dbname,
468 char *username, char *password)
470 const char *myname = "dict_pgsql_get_active";
471 HOST *host;
472 int count = RETRY_CONN_MAX;
474 /* try the active connections first; prefer the ones to UNIX sockets */
475 if ((host = dict_pgsql_find_host(PLDB, STATACTIVE, TYPEUNIX)) != NULL ||
476 (host = dict_pgsql_find_host(PLDB, STATACTIVE, TYPEINET)) != NULL) {
477 if (msg_verbose)
478 msg_info("%s: found active connection to host %s", myname,
479 host->hostname);
480 return host;
484 * Try the remaining hosts.
485 * "count" is a safety net, in case the loop takes more than
486 * RETRY_CONN_INTV and the dead hosts are no longer skipped.
488 while (--count > 0 &&
489 ((host = dict_pgsql_find_host(PLDB, STATUNTRIED | STATFAIL,
490 TYPEUNIX)) != NULL ||
491 (host = dict_pgsql_find_host(PLDB, STATUNTRIED | STATFAIL,
492 TYPEINET)) != NULL)) {
493 if (msg_verbose)
494 msg_info("%s: attempting to connect to host %s", myname,
495 host->hostname);
496 plpgsql_connect_single(host, dbname, username, password);
497 if (host->stat == STATACTIVE)
498 return host;
501 /* bad news... */
502 return 0;
505 /* dict_pgsql_event - callback: close idle connections */
507 static void dict_pgsql_event(int unused_event, char *context)
509 HOST *host = (HOST *) context;
511 if (host->db)
512 plpgsql_close_host(host);
516 * plpgsql_query - process a PostgreSQL query. Return PGSQL_RES* on success.
517 * On failure, log failure and try other db instances.
518 * on failure of all db instances, return 0;
519 * close unnecessary active connections
522 static PGSQL_RES *plpgsql_query(DICT_PGSQL *dict_pgsql,
523 const char *name,
524 VSTRING *query,
525 char *dbname,
526 char *username,
527 char *password)
529 PLPGSQL *PLDB = dict_pgsql->pldb;
530 HOST *host;
531 PGSQL_RES *res = 0;
532 ExecStatusType status;
534 while ((host = dict_pgsql_get_active(PLDB, dbname, username, password)) != NULL) {
536 * The active host is used to escape strings in the
537 * context of the active connection's character encoding.
539 dict_pgsql->active_host = host;
540 VSTRING_RESET(query);
541 VSTRING_TERMINATE(query);
542 db_common_expand(dict_pgsql->ctx, dict_pgsql->query,
543 name, 0, query, dict_pgsql_quote);
544 dict_pgsql->active_host = 0;
546 /* Check for potential dict_pgsql_quote() failure. */
547 if (host->stat == STATFAIL) {
548 plpgsql_down_host(host);
549 continue;
553 * Submit a command to the server. Be paranoid when processing
554 * the result set: try to enumerate every successful case, and
555 * reject everything else.
557 * From PostgreSQL 8.1.4 docs: (PQexec) returns a PGresult
558 * pointer or possibly a null pointer. A non-null pointer will
559 * generally be returned except in out-of-memory conditions or
560 * serious errors such as inability to send the command to the
561 * server.
563 if ((res = PQexec(host->db, vstring_str(query))) != 0) {
565 * XXX Because non-null result pointer does not imply success,
566 * we need to check the command's result status.
568 * Section 28.3.1: A result of status PGRES_NONFATAL_ERROR
569 * will never be returned directly by PQexec or other query
570 * execution functions; results of this kind are instead
571 * passed to the notice processor.
573 * PGRES_EMPTY_QUERY is being sent by the server when the
574 * query string is empty. The sanity-checking done by
575 * the Postfix infrastructure makes this case impossible,
576 * so we need not handle this situation explicitly.
578 switch ((status = PQresultStatus(res))) {
579 case PGRES_TUPLES_OK:
580 case PGRES_COMMAND_OK:
581 /* Success. */
582 if (msg_verbose)
583 msg_info("dict_pgsql: successful query from host %s",
584 host->hostname);
585 event_request_timer(dict_pgsql_event, (char *) host,
586 IDLE_CONN_INTV);
587 return (res);
588 case PGRES_FATAL_ERROR:
589 msg_warn("pgsql query failed: fatal error from host %s: %s",
590 host->hostname, PQresultErrorMessage(res));
591 break;
592 case PGRES_BAD_RESPONSE:
593 msg_warn("pgsql query failed: protocol error, host %s",
594 host->hostname);
595 break;
596 default:
597 msg_warn("pgsql query failed: unknown code 0x%lx from host %s",
598 (unsigned long) status, host->hostname);
599 break;
601 } else {
603 * This driver treats null pointers like fatal, non-null
604 * result pointer errors, as suggested by the PostgreSQL
605 * 8.1.4 documentation.
607 msg_warn("pgsql query failed: fatal error from host %s: %s",
608 host->hostname, PQerrorMessage(host->db));
612 * XXX An error occurred. Clean up memory and skip this connection.
614 if (res != 0)
615 PQclear(res);
616 plpgsql_down_host(host);
619 return (0);
623 * plpgsql_connect_single -
624 * used to reconnect to a single database when one is down or none is
625 * connected yet. Log all errors and set the stat field of host accordingly
627 static void plpgsql_connect_single(HOST *host, char *dbname, char *username, char *password)
629 if ((host->db = PQsetdbLogin(host->name, host->port, NULL, NULL,
630 dbname, username, password)) == NULL
631 || PQstatus(host->db) != CONNECTION_OK) {
632 msg_warn("connect to pgsql server %s: %s",
633 host->hostname, PQerrorMessage(host->db));
634 plpgsql_down_host(host);
635 return;
638 if (msg_verbose)
639 msg_info("dict_pgsql: successful connection to host %s",
640 host->hostname);
643 * XXX Postfix does not send multi-byte characters. The following
644 * piece of code is an explicit statement of this fact, and the
645 * database server should not accept multi-byte information after
646 * this point.
648 if (PQsetClientEncoding(host->db, "LATIN1") != 0) {
649 msg_warn("dict_pgsql: cannot set the encoding to LATIN1, skipping %s",
650 host->hostname);
651 plpgsql_down_host(host);
652 return;
655 /* Success. */
656 host->stat = STATACTIVE;
659 /* plpgsql_close_host - close an established PostgreSQL connection */
661 static void plpgsql_close_host(HOST *host)
663 if (host->db)
664 PQfinish(host->db);
665 host->db = 0;
666 host->stat = STATUNTRIED;
670 * plpgsql_down_host - close a failed connection AND set a "stay away from
671 * this host" timer.
673 static void plpgsql_down_host(HOST *host)
675 if (host->db)
676 PQfinish(host->db);
677 host->db = 0;
678 host->ts = time((time_t *) 0) + RETRY_CONN_INTV;
679 host->stat = STATFAIL;
680 event_cancel_timer(dict_pgsql_event, (char *) host);
683 /* pgsql_parse_config - parse pgsql configuration file */
685 static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
687 const char *myname = "pgsql_parse_config";
688 CFG_PARSER *p;
689 char *hosts;
690 VSTRING *query;
691 char *select_function;
693 p = dict_pgsql->parser = cfg_parser_alloc(pgsqlcf);
694 dict_pgsql->username = cfg_get_str(p, "user", "", 0, 0);
695 dict_pgsql->password = cfg_get_str(p, "password", "", 0, 0);
696 dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
697 dict_pgsql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
699 * XXX: The default should be non-zero for safety, but that is not
700 * backwards compatible.
702 dict_pgsql->expansion_limit = cfg_get_int(dict_pgsql->parser,
703 "expansion_limit", 0, 0, 0);
705 if ((dict_pgsql->query = cfg_get_str(p, "query", 0, 0, 0)) == 0) {
707 * No query specified -- fallback to building it from components
708 * ( old style "select %s from %s where %s" )
710 query = vstring_alloc(64);
711 select_function = cfg_get_str(p, "select_function", 0, 0, 0);
712 if (select_function != 0) {
713 vstring_sprintf(query, "SELECT %s('%%s')", select_function);
714 myfree(select_function);
715 } else
716 db_common_sql_build_query(query, p);
717 dict_pgsql->query = vstring_export(query);
721 * Must parse all templates before we can use db_common_expand()
723 dict_pgsql->ctx = 0;
724 (void) db_common_parse(&dict_pgsql->dict, &dict_pgsql->ctx,
725 dict_pgsql->query, 1);
726 (void) db_common_parse(0, &dict_pgsql->ctx, dict_pgsql->result_format, 0);
727 db_common_parse_domain(p, dict_pgsql->ctx);
730 * Maps that use substring keys should only be used with the full
731 * input key.
733 if (db_common_dict_partial(dict_pgsql->ctx))
734 dict_pgsql->dict.flags |= DICT_FLAG_PATTERN;
735 else
736 dict_pgsql->dict.flags |= DICT_FLAG_FIXED;
737 if (dict_pgsql->dict.flags & DICT_FLAG_FOLD_FIX)
738 dict_pgsql->dict.fold_buf = vstring_alloc(10);
740 hosts = cfg_get_str(p, "hosts", "", 0, 0);
742 dict_pgsql->hosts = argv_split(hosts, " ,\t\r\n");
743 if (dict_pgsql->hosts->argc == 0) {
744 argv_add(dict_pgsql->hosts, "localhost", ARGV_END);
745 argv_terminate(dict_pgsql->hosts);
746 if (msg_verbose)
747 msg_info("%s: %s: no hostnames specified, defaulting to '%s'",
748 myname, pgsqlcf, dict_pgsql->hosts->argv[0]);
750 myfree(hosts);
753 /* dict_pgsql_open - open PGSQL data base */
755 DICT *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
757 DICT_PGSQL *dict_pgsql;
759 if (open_flags != O_RDONLY)
760 msg_fatal("%s:%s map requires O_RDONLY access mode",
761 DICT_TYPE_PGSQL, name);
763 dict_pgsql = (DICT_PGSQL *) dict_alloc(DICT_TYPE_PGSQL, name,
764 sizeof(DICT_PGSQL));
765 dict_pgsql->dict.lookup = dict_pgsql_lookup;
766 dict_pgsql->dict.close = dict_pgsql_close;
767 dict_pgsql->dict.flags = dict_flags;
768 pgsql_parse_config(dict_pgsql, name);
769 dict_pgsql->active_host = 0;
770 dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts);
771 if (dict_pgsql->pldb == NULL)
772 msg_fatal("couldn't intialize pldb!\n");
773 return &dict_pgsql->dict;
776 /* plpgsql_init - initalize a PGSQL database */
778 static PLPGSQL *plpgsql_init(ARGV *hosts)
780 PLPGSQL *PLDB;
781 int i;
783 PLDB = (PLPGSQL *) mymalloc(sizeof(PLPGSQL));
784 PLDB->len_hosts = hosts->argc;
785 PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * hosts->argc);
786 for (i = 0; i < hosts->argc; i++)
787 PLDB->db_hosts[i] = host_init(hosts->argv[i]);
789 return PLDB;
793 /* host_init - initialize HOST structure */
795 static HOST *host_init(const char *hostname)
797 const char *myname = "pgsql host_init";
798 HOST *host = (HOST *) mymalloc(sizeof(HOST));
799 const char *d = hostname;
801 host->db = 0;
802 host->hostname = mystrdup(hostname);
803 host->stat = STATUNTRIED;
804 host->ts = 0;
807 * Ad-hoc parsing code. Expect "unix:pathname" or "inet:host:port", where
808 * both "inet:" and ":port" are optional.
810 if (strncmp(d, "unix:", 5) == 0 || strncmp(d, "inet:", 5) == 0)
811 d += 5;
812 host->name = mystrdup(d);
813 host->port = split_at_right(host->name, ':');
815 /* This is how PgSQL distinguishes between UNIX and INET: */
816 if (host->name[0] && host->name[0] != '/')
817 host->type = TYPEINET;
818 else
819 host->type = TYPEUNIX;
821 if (msg_verbose > 1)
822 msg_info("%s: host=%s, port=%s, type=%s", myname, host->name,
823 host->port ? host->port : "",
824 host->type == TYPEUNIX ? "unix" : "inet");
825 return host;
828 /* dict_pgsql_close - close PGSQL data base */
830 static void dict_pgsql_close(DICT *dict)
832 DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
834 plpgsql_dealloc(dict_pgsql->pldb);
835 cfg_parser_free(dict_pgsql->parser);
836 myfree(dict_pgsql->username);
837 myfree(dict_pgsql->password);
838 myfree(dict_pgsql->dbname);
839 myfree(dict_pgsql->query);
840 myfree(dict_pgsql->result_format);
841 if (dict_pgsql->hosts)
842 argv_free(dict_pgsql->hosts);
843 if (dict_pgsql->ctx)
844 db_common_free_ctx(dict_pgsql->ctx);
845 if (dict->fold_buf)
846 vstring_free(dict->fold_buf);
847 dict_free(dict);
850 /* plpgsql_dealloc - free memory associated with PLPGSQL close databases */
852 static void plpgsql_dealloc(PLPGSQL *PLDB)
854 int i;
856 for (i = 0; i < PLDB->len_hosts; i++) {
857 event_cancel_timer(dict_pgsql_event, (char *) (PLDB->db_hosts[i]));
858 if (PLDB->db_hosts[i]->db)
859 PQfinish(PLDB->db_hosts[i]->db);
860 myfree(PLDB->db_hosts[i]->hostname);
861 myfree(PLDB->db_hosts[i]->name);
862 myfree((char *) PLDB->db_hosts[i]);
864 myfree((char *) PLDB->db_hosts);
865 myfree((char *) (PLDB));
868 #endif