postgresql_default.conf, postgresql plugin: Split default queries by category.
[collectd.git] / src / postgresql.c
1 /**
2  * collectd - src/postgresql.c
3  * Copyright (C) 2008  Sebastian Harl
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Author:
19  *   Sebastian Harl <sh at tokkee.org>
20  **/
21
22 /*
23  * This module collects PostgreSQL database statistics.
24  */
25
26 #include "collectd.h"
27 #include "common.h"
28
29 #include "configfile.h"
30 #include "plugin.h"
31
32 #include "utils_complain.h"
33
34 #include <pg_config_manual.h>
35 #include <libpq-fe.h>
36
37 #define log_err(...) ERROR ("postgresql: " __VA_ARGS__)
38 #define log_warn(...) WARNING ("postgresql: " __VA_ARGS__)
39 #define log_info(...) INFO ("postgresql: " __VA_ARGS__)
40
41 #ifndef C_PSQL_DEFAULT_CONF
42 # define C_PSQL_DEFAULT_CONF PKGDATADIR "/postgresql_default.conf"
43 #endif
44
45 /* Appends the (parameter, value) pair to the string
46  * pointed to by 'buf' suitable to be used as argument
47  * for PQconnectdb(). If value equals NULL, the pair
48  * is ignored. */
49 #define C_PSQL_PAR_APPEND(buf, buf_len, parameter, value) \
50         if ((0 < (buf_len)) && (NULL != (value)) && ('\0' != *(value))) { \
51                 int s = ssnprintf (buf, buf_len, " %s = '%s'", parameter, value); \
52                 if (0 < s) { \
53                         buf     += s; \
54                         buf_len -= s; \
55                 } \
56         }
57
58 /* Returns the tuple (major, minor, patchlevel)
59  * for the given version number. */
60 #define C_PSQL_SERVER_VERSION3(server_version) \
61         (server_version) / 10000, \
62         (server_version) / 100 - (int)((server_version) / 10000) * 100, \
63         (server_version) - (int)((server_version) / 100) * 100
64
65 /* Returns true if the given host specifies a
66  * UNIX domain socket. */
67 #define C_PSQL_IS_UNIX_DOMAIN_SOCKET(host) \
68         ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host)))
69
70 /* Returns the tuple (host, delimiter, port) for a
71  * given (host, port) pair. Depending on the value of
72  * 'host' a UNIX domain socket or a TCP socket is
73  * assumed. */
74 #define C_PSQL_SOCKET3(host, port) \
75         ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \
76         C_PSQL_IS_UNIX_DOMAIN_SOCKET (host) ? "/.s.PGSQL." : ":", \
77         port
78
79 typedef enum {
80         C_PSQL_PARAM_HOST = 1,
81         C_PSQL_PARAM_DB,
82         C_PSQL_PARAM_USER,
83 } c_psql_param_t;
84
85 typedef struct {
86         char *type;
87         char *type_instance;
88         int   ds_type;
89 } c_psql_col_t;
90
91 typedef struct {
92         char *name;
93         char *query;
94
95         c_psql_param_t *params;
96         int             params_num;
97
98         c_psql_col_t *cols;
99         int           cols_num;
100 } c_psql_query_t;
101
102 typedef struct {
103         PGconn      *conn;
104         c_complain_t conn_complaint;
105
106         int max_params_num;
107
108         /* user configuration */
109         c_psql_query_t **queries;
110         int              queries_num;
111
112         char *host;
113         char *port;
114         char *database;
115         char *user;
116         char *password;
117
118         char *sslmode;
119
120         char *krbsrvname;
121
122         char *service;
123 } c_psql_database_t;
124
125 static char *def_queries[] = {
126         "backends",
127         "transactions",
128         "queries",
129         "query_plans",
130         "table_states",
131         "disk_io"
132 };
133 static int def_queries_num = STATIC_ARRAY_SIZE (def_queries);
134
135 static c_psql_query_t *queries          = NULL;
136 static int             queries_num      = 0;
137
138 static c_psql_database_t *databases     = NULL;
139 static int                databases_num = 0;
140
141 static c_psql_query_t *c_psql_query_new (const char *name)
142 {
143         c_psql_query_t *query;
144
145         ++queries_num;
146         if (NULL == (queries = (c_psql_query_t *)realloc (queries,
147                                 queries_num * sizeof (*queries)))) {
148                 log_err ("Out of memory.");
149                 exit (5);
150         }
151         query = queries + queries_num - 1;
152
153         query->name  = sstrdup (name);
154         query->query = NULL;
155
156         query->params     = NULL;
157         query->params_num = 0;
158
159         query->cols     = NULL;
160         query->cols_num = 0;
161         return query;
162 } /* c_psql_query_new */
163
164 static void c_psql_query_delete (c_psql_query_t *query)
165 {
166         int i;
167
168         sfree (query->name);
169         sfree (query->query);
170
171         sfree (query->params);
172         query->params_num = 0;
173
174         for (i = 0; i < query->cols_num; ++i) {
175                 sfree (query->cols[i].type);
176                 sfree (query->cols[i].type_instance);
177         }
178         sfree (query->cols);
179         query->cols_num = 0;
180         return;
181 } /* c_psql_query_delete */
182
183 static c_psql_query_t *c_psql_query_get (const char *name)
184 {
185         int i;
186
187         for (i = 0; i < queries_num; ++i)
188                 if (0 == strcasecmp (name, queries[i].name))
189                         return queries + i;
190         return NULL;
191 } /* c_psql_query_get */
192
193 static c_psql_database_t *c_psql_database_new (const char *name)
194 {
195         c_psql_database_t *db;
196
197         ++databases_num;
198         if (NULL == (databases = (c_psql_database_t *)realloc (databases,
199                                 databases_num * sizeof (*databases)))) {
200                 log_err ("Out of memory.");
201                 exit (5);
202         }
203
204         db = databases + (databases_num - 1);
205
206         db->conn = NULL;
207
208         db->conn_complaint.last     = 0;
209         db->conn_complaint.interval = 0;
210
211         db->max_params_num = 0;
212
213         db->queries     = NULL;
214         db->queries_num = 0;
215
216         db->database   = sstrdup (name);
217         db->host       = NULL;
218         db->port       = NULL;
219         db->user       = NULL;
220         db->password   = NULL;
221
222         db->sslmode    = NULL;
223
224         db->krbsrvname = NULL;
225
226         db->service    = NULL;
227         return db;
228 } /* c_psql_database_new */
229
230 static void c_psql_database_delete (c_psql_database_t *db)
231 {
232         PQfinish (db->conn);
233
234         sfree (db->queries);
235         db->queries_num = 0;
236
237         sfree (db->database);
238         sfree (db->host);
239         sfree (db->port);
240         sfree (db->user);
241         sfree (db->password);
242
243         sfree (db->sslmode);
244
245         sfree (db->krbsrvname);
246
247         sfree (db->service);
248         return;
249 } /* c_psql_database_delete */
250
251 static void submit (const c_psql_database_t *db,
252                 const char *type, const char *type_instance,
253                 value_t *values, size_t values_len)
254 {
255         value_list_t vl = VALUE_LIST_INIT;
256
257         vl.values     = values;
258         vl.values_len = values_len;
259         vl.time       = time (NULL);
260
261         if (C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host)
262                         || (0 == strcmp (db->host, "localhost")))
263                 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
264         else
265                 sstrncpy (vl.host, db->host, sizeof (vl.host));
266
267         sstrncpy (vl.plugin, "postgresql", sizeof (vl.plugin));
268         sstrncpy (vl.plugin_instance, db->database, sizeof (vl.plugin_instance));
269
270         sstrncpy (vl.type, type, sizeof (vl.type));
271
272         if (NULL != type_instance)
273                 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
274
275         plugin_dispatch_values (&vl);
276         return;
277 } /* submit */
278
279 static void submit_counter (const c_psql_database_t *db,
280                 const char *type, const char *type_instance,
281                 const char *value)
282 {
283         value_t values[1];
284
285         if ((NULL == value) || ('\0' == *value))
286                 return;
287
288         values[0].counter = atoll (value);
289         submit (db, type, type_instance, values, 1);
290         return;
291 } /* submit_counter */
292
293 static void submit_gauge (const c_psql_database_t *db,
294                 const char *type, const char *type_instance,
295                 const char *value)
296 {
297         value_t values[1];
298
299         if ((NULL == value) || ('\0' == *value))
300                 return;
301
302         values[0].gauge = atof (value);
303         submit (db, type, type_instance, values, 1);
304         return;
305 } /* submit_gauge */
306
307 static int c_psql_check_connection (c_psql_database_t *db)
308 {
309         /* "ping" */
310         PQclear (PQexec (db->conn, "SELECT 42;"));
311
312         if (CONNECTION_OK != PQstatus (db->conn)) {
313                 PQreset (db->conn);
314
315                 /* trigger c_release() */
316                 if (0 == db->conn_complaint.interval)
317                         db->conn_complaint.interval = 1;
318
319                 if (CONNECTION_OK != PQstatus (db->conn)) {
320                         c_complain (LOG_ERR, &db->conn_complaint,
321                                         "Failed to connect to database %s: %s",
322                                         db->database, PQerrorMessage (db->conn));
323                         return -1;
324                 }
325         }
326
327         c_release (LOG_INFO, &db->conn_complaint,
328                         "Successfully reconnected to database %s", PQdb (db->conn));
329         return 0;
330 } /* c_psql_check_connection */
331
332 static int c_psql_exec_query (c_psql_database_t *db, int idx)
333 {
334         c_psql_query_t *query;
335         PGresult       *res;
336
337         char *params[db->max_params_num];
338
339         int rows, cols;
340         int i;
341
342         if (idx >= db->queries_num)
343                 return -1;
344
345         query = db->queries[idx];
346
347         assert (db->max_params_num >= query->params_num);
348
349         for (i = 0; i < query->params_num; ++i) {
350                 switch (query->params[i]) {
351                         case C_PSQL_PARAM_HOST:
352                                 params[i] = C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host)
353                                         ? "localhost" : db->host;
354                                 break;
355                         case C_PSQL_PARAM_DB:
356                                 params[i] = db->database;
357                                 break;
358                         case C_PSQL_PARAM_USER:
359                                 params[i] = db->user;
360                                 break;
361                         default:
362                                 assert (0);
363                 }
364         }
365
366         res = PQexecParams (db->conn, query->query, query->params_num, NULL,
367                         (const char *const *)((0 == query->params_num) ? NULL : params),
368                         NULL, NULL, /* return text data */ 0);
369
370         if (PGRES_TUPLES_OK != PQresultStatus (res)) {
371                 log_err ("Failed to execute SQL query: %s",
372                                 PQerrorMessage (db->conn));
373                 log_info ("SQL query was: %s", query->query);
374                 PQclear (res);
375                 return -1;
376         }
377
378         rows = PQntuples (res);
379         if (1 > rows)
380                 return 0;
381
382         cols = PQnfields (res);
383         if (query->cols_num != cols) {
384                 log_err ("SQL query returned wrong number of fields "
385                                 "(expected: %i, got: %i)", query->cols_num, cols);
386                 log_info ("SQL query was: %s", query->query);
387                 return -1;
388         }
389
390         for (i = 0; i < rows; ++i) {
391                 int j;
392
393                 for (j = 0; j < cols; ++j) {
394                         c_psql_col_t col = query->cols[j];
395
396                         char *value = PQgetvalue (res, i, j);
397
398                         if (col.ds_type == DS_TYPE_COUNTER)
399                                 submit_counter (db, col.type, col.type_instance, value);
400                         else if (col.ds_type == DS_TYPE_GAUGE)
401                                 submit_gauge (db, col.type, col.type_instance, value);
402                 }
403         }
404         return 0;
405 } /* c_psql_exec_query */
406
407 static int c_psql_read (void)
408 {
409         int success = 0;
410         int i;
411
412         for (i = 0; i < databases_num; ++i) {
413                 c_psql_database_t *db = databases + i;
414
415                 int j;
416
417                 assert (NULL != db->database);
418
419                 if (0 != c_psql_check_connection (db))
420                         continue;
421
422                 for (j = 0; j < db->queries_num; ++j)
423                         c_psql_exec_query (db, j);
424
425                 ++success;
426         }
427
428         if (! success)
429                 return -1;
430         return 0;
431 } /* c_psql_read */
432
433 static int c_psql_shutdown (void)
434 {
435         int i;
436
437         if ((NULL == databases) || (0 == databases_num))
438                 return 0;
439
440         plugin_unregister_read ("postgresql");
441         plugin_unregister_shutdown ("postgresql");
442
443         for (i = 0; i < databases_num; ++i) {
444                 c_psql_database_t *db = databases + i;
445                 c_psql_database_delete (db);
446         }
447
448         sfree (databases);
449         databases_num = 0;
450
451         for (i = 0; i < queries_num; ++i) {
452                 c_psql_query_t *query = queries + i;
453                 c_psql_query_delete (query);
454         }
455
456         sfree (queries);
457         queries_num = 0;
458         return 0;
459 } /* c_psql_shutdown */
460
461 static int c_psql_init (void)
462 {
463         int i;
464
465         if ((NULL == databases) || (0 == databases_num))
466                 return 0;
467
468         for (i = 0; i < queries_num; ++i) {
469                 c_psql_query_t *query = queries + i;
470                 int j;
471
472                 for (j = 0; j < query->cols_num; ++j) {
473                         c_psql_col_t     *col = query->cols + j;
474                         const data_set_t *ds;
475
476                         ds = plugin_get_ds (col->type);
477                         if (NULL == ds) {
478                                 log_err ("Column: Unknown type \"%s\".", col->type);
479                                 c_psql_shutdown ();
480                                 return -1;
481                         }
482
483                         if (1 != ds->ds_num) {
484                                 log_err ("Column: Invalid type \"%s\" - types defining "
485                                                 "one data source are supported only (got: %i).",
486                                                 col->type, ds->ds_num);
487                                 c_psql_shutdown ();
488                                 return -1;
489                         }
490
491                         col->ds_type = ds->ds[0].type;
492                 }
493         }
494
495         for (i = 0; i < databases_num; ++i) {
496                 c_psql_database_t *db = databases + i;
497
498                 char  conninfo[4096];
499                 char *buf     = conninfo;
500                 int   buf_len = sizeof (conninfo);
501                 int   status;
502
503                 char *server_host;
504                 int   server_version;
505
506                 status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
507                 if (0 < status) {
508                         buf     += status;
509                         buf_len -= status;
510                 }
511
512                 C_PSQL_PAR_APPEND (buf, buf_len, "host",       db->host);
513                 C_PSQL_PAR_APPEND (buf, buf_len, "port",       db->port);
514                 C_PSQL_PAR_APPEND (buf, buf_len, "user",       db->user);
515                 C_PSQL_PAR_APPEND (buf, buf_len, "password",   db->password);
516                 C_PSQL_PAR_APPEND (buf, buf_len, "sslmode",    db->sslmode);
517                 C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
518                 C_PSQL_PAR_APPEND (buf, buf_len, "service",    db->service);
519
520                 db->conn = PQconnectdb (conninfo);
521                 if (0 != c_psql_check_connection (db))
522                         continue;
523
524                 server_host    = PQhost (db->conn);
525                 server_version = PQserverVersion (db->conn);
526                 log_info ("Sucessfully connected to database %s (user %s) "
527                                 "at server %s%s%s (server version: %d.%d.%d, "
528                                 "protocol version: %d, pid: %d)",
529                                 PQdb (db->conn), PQuser (db->conn),
530                                 C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
531                                 C_PSQL_SERVER_VERSION3 (server_version),
532                                 PQprotocolVersion (db->conn), PQbackendPID (db->conn));
533         }
534
535         plugin_register_read ("postgresql", c_psql_read);
536         plugin_register_shutdown ("postgresql", c_psql_shutdown);
537         return 0;
538 } /* c_psql_init */
539
540 static int config_set (char *name, char **var, const oconfig_item_t *ci)
541 {
542         if ((0 != ci->children_num) || (1 != ci->values_num)
543                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
544                 log_err ("%s expects a single string argument.", name);
545                 return 1;
546         }
547
548         sfree (*var);
549         *var = sstrdup (ci->values[0].value.string);
550         return 0;
551 } /* config_set */
552
553 static int config_set_param (c_psql_query_t *query, const oconfig_item_t *ci)
554 {
555         c_psql_param_t param;
556         char          *param_str;
557
558         if ((0 != ci->children_num) || (1 != ci->values_num)
559                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
560                 log_err ("Param expects a single string argument.");
561                 return 1;
562         }
563
564         param_str = ci->values[0].value.string;
565         if (0 == strcasecmp (param_str, "hostname"))
566                 param = C_PSQL_PARAM_HOST;
567         else if (0 == strcasecmp (param_str, "database"))
568                 param = C_PSQL_PARAM_DB;
569         else if (0 == strcasecmp (param_str, "username"))
570                 param = C_PSQL_PARAM_USER;
571         else {
572                 log_err ("Invalid parameter \"%s\".", param_str);
573                 return 1;
574         }
575
576         ++query->params_num;
577         if (NULL == (query->params = (c_psql_param_t *)realloc (query->params,
578                                 query->params_num * sizeof (*query->params)))) {
579                 log_err ("Out of memory.");
580                 exit (5);
581         }
582
583         query->params[query->params_num - 1] = param;
584         return 0;
585 } /* config_set_param */
586
587 static int config_set_column (c_psql_query_t *query, const oconfig_item_t *ci)
588 {
589         c_psql_col_t *col;
590
591         int i;
592
593         if ((0 != ci->children_num)
594                         || (1 > ci->values_num) || (2 < ci->values_num)) {
595                 log_err ("Column expects either one or two arguments.");
596                 return 1;
597         }
598
599         for (i = 0; i < ci->values_num; ++i) {
600                 if (OCONFIG_TYPE_STRING != ci->values[i].type) {
601                         log_err ("Column expects either one or two string arguments.");
602                         return 1;
603                 }
604         }
605
606         ++query->cols_num;
607         if (NULL == (query->cols = (c_psql_col_t *)realloc (query->cols,
608                                 query->cols_num * sizeof (*query->cols)))) {
609                 log_err ("Out of memory.");
610                 exit (5);
611         }
612
613         col = query->cols + query->cols_num - 1;
614
615         col->ds_type = -1;
616
617         col->type = sstrdup (ci->values[0].value.string);
618         col->type_instance = (2 == ci->values_num)
619                 ? sstrdup (ci->values[1].value.string) : NULL;
620         return 0;
621 } /* config_set_column */
622
623 static int config_set_query (c_psql_database_t *db, const oconfig_item_t *ci)
624 {
625         c_psql_query_t *query;
626
627         if ((0 != ci->children_num) || (1 != ci->values_num)
628                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
629                 log_err ("Query expects a single string argument.");
630                 return 1;
631         }
632
633         query = c_psql_query_get (ci->values[0].value.string);
634         if (NULL == query) {
635                 log_err ("Query \"%s\" not found - please check your configuration.",
636                                 ci->values[0].value.string);
637                 return 1;
638         }
639
640         ++db->queries_num;
641         if (NULL == (db->queries = (c_psql_query_t **)realloc (db->queries,
642                                 db->queries_num * sizeof (*db->queries)))) {
643                 log_err ("Out of memory.");
644                 exit (5);
645         }
646
647         if (query->params_num > db->max_params_num)
648                 db->max_params_num = query->params_num;
649
650         db->queries[db->queries_num - 1] = query;
651         return 0;
652 } /* config_set_query */
653
654 static int c_psql_config_query (oconfig_item_t *ci)
655 {
656         c_psql_query_t *query;
657
658         int i;
659
660         if ((1 != ci->values_num)
661                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
662                 log_err ("<Query> expects a single string argument.");
663                 return 1;
664         }
665
666         query = c_psql_query_new (ci->values[0].value.string);
667
668         for (i = 0; i < ci->children_num; ++i) {
669                 oconfig_item_t *c = ci->children + i;
670
671                 if (0 == strcasecmp (c->key, "Query"))
672                         config_set ("Query", &query->query, c);
673                 else if (0 == strcasecmp (c->key, "Param"))
674                         config_set_param (query, c);
675                 else if (0 == strcasecmp (c->key, "Column"))
676                         config_set_column (query, c);
677                 else
678                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
679         }
680         return 0;
681 } /* c_psql_config_query */
682
683 static int c_psql_config_database (oconfig_item_t *ci)
684 {
685         c_psql_database_t *db;
686
687         int i;
688
689         if ((1 != ci->values_num)
690                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
691                 log_err ("<Database> expects a single string argument.");
692                 return 1;
693         }
694
695         db = c_psql_database_new (ci->values[0].value.string);
696
697         for (i = 0; i < ci->children_num; ++i) {
698                 oconfig_item_t *c = ci->children + i;
699
700                 if (0 == strcasecmp (c->key, "Host"))
701                         config_set ("Host", &db->host, c);
702                 else if (0 == strcasecmp (c->key, "Port"))
703                         config_set ("Port", &db->port, c);
704                 else if (0 == strcasecmp (c->key, "User"))
705                         config_set ("User", &db->user, c);
706                 else if (0 == strcasecmp (c->key, "Password"))
707                         config_set ("Password", &db->password, c);
708                 else if (0 == strcasecmp (c->key, "SSLMode"))
709                         config_set ("SSLMode", &db->sslmode, c);
710                 else if (0 == strcasecmp (c->key, "KRBSrvName"))
711                         config_set ("KRBSrvName", &db->krbsrvname, c);
712                 else if (0 == strcasecmp (c->key, "Service"))
713                         config_set ("Service", &db->service, c);
714                 else if (0 == strcasecmp (c->key, "Query"))
715                         config_set_query (db, c);
716                 else
717                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
718         }
719
720         if (NULL == db->queries) {
721                 db->queries = (c_psql_query_t **)malloc (def_queries_num
722                                 * sizeof (*db->queries));
723
724                 for (i = 0; i < def_queries_num; ++i) {
725                         db->queries[i] = c_psql_query_get (def_queries[i]);
726                         if (NULL == db->queries[i])
727                                 log_err ("Query \"%s\" not found - "
728                                                 "please check your installation.",
729                                                 def_queries[i]);
730                         else
731                                 ++db->queries_num;
732                 }
733         }
734         return 0;
735 }
736
737 static int c_psql_config (oconfig_item_t *ci)
738 {
739         static int have_def_config = 0;
740
741         int i;
742
743         if (0 == have_def_config) {
744                 oconfig_item_t *c;
745
746                 have_def_config = 1;
747
748                 c = oconfig_parse_file (C_PSQL_DEFAULT_CONF);
749                 if (NULL == c)
750                         log_err ("Failed to read default config ("C_PSQL_DEFAULT_CONF").");
751                 else
752                         c_psql_config (c);
753
754                 if (NULL == queries)
755                         log_err ("Default config ("C_PSQL_DEFAULT_CONF") did not define "
756                                         "any queries - please check your installation.");
757         }
758
759         for (i = 0; i < ci->children_num; ++i) {
760                 oconfig_item_t *c = ci->children + i;
761
762                 if (0 == strcasecmp (c->key, "Query"))
763                         c_psql_config_query (c);
764                 else if (0 == strcasecmp (c->key, "Database"))
765                         c_psql_config_database (c);
766                 else
767                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
768         }
769         return 0;
770 } /* c_psql_config */
771
772 void module_register (void)
773 {
774         plugin_register_complex_config ("postgresql", c_psql_config);
775         plugin_register_init ("postgresql", c_psql_init);
776 } /* module_register */
777
778 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
779