7 /* dictionary manager interface to PostgreSQL databases
9 /* #include <dict_pgsql.h>
11 /* DICT *dict_pgsql_open(name, open_flags, dict_flags)
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
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.
45 /* reference for outside use.
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:
58 /* Username for connecting to the database.
60 /* Password for the above.
62 /* Name of the database.
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).
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
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.
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).
102 /* List of hosts to connect to.
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
112 /* \fIuser\fR = \fBpostfix\fR
114 /* \fIpassword\fR = \fBpasswd\fR
116 /* \fIdbname\fR = \fBpostfix_info\fR
118 /* \fItable\fR = \fBaliases\fR
120 /* \fIselect_field\fR = \fBforw_addr\fR
122 /* \fIwhere_field\fR = \fBalias\fR
124 /* \fIhosts\fR = \fBhost1.some.domain\fR \fBhost2.some.domain\fR
127 /* dict(3) generic dictionary manager
130 /* androsyn@ratbox.org
132 /* Based upon dict_mysql.c by
143 /* System library. */
145 #include "sys_defs.h"
148 #include <sys/socket.h>
149 #include <netinet/in.h>
150 #include <arpa/inet.h>
158 #include <postgres_ext.h>
159 #include <libpq-fe.h>
161 /* Utility library. */
165 #include "mymalloc.h"
168 #include "split_at.h"
169 #include "find_inet.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 */
199 unsigned type
; /* TYPEUNIX | TYPEINET */
200 unsigned stat
; /* STATUNTRIED | STATFAIL | STATCUR */
201 time_t ts
; /* used for attempting reconnection */
205 int len_hosts
; /* number of hosts */
206 HOST
**db_hosts
; /* hosts on which databases reside */
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 *,
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;
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.
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
)
273 * Escape the input string, using PQescapeStringConn(), because
274 * the older PQescapeString() is not safe anymore, as stated by the
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.
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.
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.
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
);
307 VSTRING_SKIP(result
);
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
;
329 static VSTRING
*query
;
330 static VSTRING
*result
;
338 dict_pgsql
= (DICT_PGSQL
*) dict
;
339 pldb
= dict_pgsql
->pldb
;
341 #define INIT_VSTR(buf, len) do { \
343 buf = vstring_alloc(len); \
344 VSTRING_RESET(buf); \
345 VSTRING_TERMINATE(buf); \
348 INIT_VSTR(query
, 10);
349 INIT_VSTR(result
, 10);
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) {
370 msg_info("%s: Skipping lookup of '%s'", myname
, name
);
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
,
386 /* do the query - set dict_errno & cleanup if there's an error */
387 if ((query_res
= plpgsql_query(dict_pgsql
, name
, query
,
389 dict_pgsql
->username
,
390 dict_pgsql
->password
)) == 0) {
391 dict_errno
= DICT_ERR_RETRY
;
395 numrows
= PQntuples(query_res
);
397 msg_info("%s: retrieved %d rows", myname
, numrows
);
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
,
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
;
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
,
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
)
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
)
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
))
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
) &&
459 return PLDB
->db_hosts
[i
];
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";
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
) {
478 msg_info("%s: found active connection to host %s", myname
,
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
)) {
494 msg_info("%s: attempting to connect to host %s", myname
,
496 plpgsql_connect_single(host
, dbname
, username
, password
);
497 if (host
->stat
== STATACTIVE
)
505 /* dict_pgsql_event - callback: close idle connections */
507 static void dict_pgsql_event(int unused_event
, char *context
)
509 HOST
*host
= (HOST
*) context
;
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
,
529 PLPGSQL
*PLDB
= dict_pgsql
->pldb
;
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
);
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
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
:
583 msg_info("dict_pgsql: successful query from host %s",
585 event_request_timer(dict_pgsql_event
, (char *) host
,
588 case PGRES_FATAL_ERROR
:
589 msg_warn("pgsql query failed: fatal error from host %s: %s",
590 host
->hostname
, PQresultErrorMessage(res
));
592 case PGRES_BAD_RESPONSE
:
593 msg_warn("pgsql query failed: protocol error, host %s",
597 msg_warn("pgsql query failed: unknown code 0x%lx from host %s",
598 (unsigned long) status
, host
->hostname
);
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.
616 plpgsql_down_host(host
);
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
);
639 msg_info("dict_pgsql: successful connection to host %s",
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
648 if (PQsetClientEncoding(host
->db
, "LATIN1") != 0) {
649 msg_warn("dict_pgsql: cannot set the encoding to LATIN1, skipping %s",
651 plpgsql_down_host(host
);
656 host
->stat
= STATACTIVE
;
659 /* plpgsql_close_host - close an established PostgreSQL connection */
661 static void plpgsql_close_host(HOST
*host
)
666 host
->stat
= STATUNTRIED
;
670 * plpgsql_down_host - close a failed connection AND set a "stay away from
673 static void plpgsql_down_host(HOST
*host
)
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";
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
);
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()
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
733 if (db_common_dict_partial(dict_pgsql
->ctx
))
734 dict_pgsql
->dict
.flags
|= DICT_FLAG_PATTERN
;
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
);
747 msg_info("%s: %s: no hostnames specified, defaulting to '%s'",
748 myname
, pgsqlcf
, dict_pgsql
->hosts
->argv
[0]);
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
,
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
)
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
]);
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
;
802 host
->hostname
= mystrdup(hostname
);
803 host
->stat
= STATUNTRIED
;
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)
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
;
819 host
->type
= TYPEUNIX
;
822 msg_info("%s: host=%s, port=%s, type=%s", myname
, host
->name
,
823 host
->port
? host
->port
: "",
824 host
->type
== TYPEUNIX
? "unix" : "inet");
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
);
844 db_common_free_ctx(dict_pgsql
->ctx
);
846 vstring_free(dict
->fold_buf
);
850 /* plpgsql_dealloc - free memory associated with PLPGSQL close databases */
852 static void plpgsql_dealloc(PLPGSQL
*PLDB
)
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
));