postgresql plugin: Added support for passing parameters to a query.
[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         "user_tables",
127         "io_user_tables"
128 };
129 static int def_queries_num = STATIC_ARRAY_SIZE (def_queries);
130
131 static c_psql_query_t *queries          = NULL;
132 static int             queries_num      = 0;
133
134 static c_psql_database_t *databases     = NULL;
135 static int                databases_num = 0;
136
137 static c_psql_query_t *c_psql_query_new (const char *name)
138 {
139         c_psql_query_t *query;
140
141         ++queries_num;
142         if (NULL == (queries = (c_psql_query_t *)realloc (queries,
143                                 queries_num * sizeof (*queries)))) {
144                 log_err ("Out of memory.");
145                 exit (5);
146         }
147         query = queries + queries_num - 1;
148
149         query->name  = sstrdup (name);
150         query->query = NULL;
151
152         query->params     = NULL;
153         query->params_num = 0;
154
155         query->cols     = NULL;
156         query->cols_num = 0;
157         return query;
158 } /* c_psql_query_new */
159
160 static void c_psql_query_delete (c_psql_query_t *query)
161 {
162         int i;
163
164         sfree (query->name);
165         sfree (query->query);
166
167         sfree (query->params);
168         query->params_num = 0;
169
170         for (i = 0; i < query->cols_num; ++i) {
171                 sfree (query->cols[i].type);
172                 sfree (query->cols[i].type_instance);
173         }
174         sfree (query->cols);
175         query->cols_num = 0;
176         return;
177 } /* c_psql_query_delete */
178
179 static c_psql_query_t *c_psql_query_get (const char *name)
180 {
181         int i;
182
183         for (i = 0; i < queries_num; ++i)
184                 if (0 == strcasecmp (name, queries[i].name))
185                         return queries + i;
186         return NULL;
187 } /* c_psql_query_get */
188
189 static c_psql_database_t *c_psql_database_new (const char *name)
190 {
191         c_psql_database_t *db;
192
193         ++databases_num;
194         if (NULL == (databases = (c_psql_database_t *)realloc (databases,
195                                 databases_num * sizeof (*databases)))) {
196                 log_err ("Out of memory.");
197                 exit (5);
198         }
199
200         db = databases + (databases_num - 1);
201
202         db->conn = NULL;
203
204         db->conn_complaint.last     = 0;
205         db->conn_complaint.interval = 0;
206
207         db->max_params_num = 0;
208
209         db->queries     = NULL;
210         db->queries_num = 0;
211
212         db->database   = sstrdup (name);
213         db->host       = NULL;
214         db->port       = NULL;
215         db->user       = NULL;
216         db->password   = NULL;
217
218         db->sslmode    = NULL;
219
220         db->krbsrvname = NULL;
221
222         db->service    = NULL;
223         return db;
224 } /* c_psql_database_new */
225
226 static void c_psql_database_delete (c_psql_database_t *db)
227 {
228         PQfinish (db->conn);
229
230         sfree (db->queries);
231         db->queries_num = 0;
232
233         sfree (db->database);
234         sfree (db->host);
235         sfree (db->port);
236         sfree (db->user);
237         sfree (db->password);
238
239         sfree (db->sslmode);
240
241         sfree (db->krbsrvname);
242
243         sfree (db->service);
244         return;
245 } /* c_psql_database_delete */
246
247 static void submit (const c_psql_database_t *db,
248                 const char *type, const char *type_instance,
249                 value_t *values, size_t values_len)
250 {
251         value_list_t vl = VALUE_LIST_INIT;
252
253         vl.values     = values;
254         vl.values_len = values_len;
255         vl.time       = time (NULL);
256
257         if (C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host)
258                         || (0 == strcmp (db->host, "localhost")))
259                 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
260         else
261                 sstrncpy (vl.host, db->host, sizeof (vl.host));
262
263         sstrncpy (vl.plugin, "postgresql", sizeof (vl.plugin));
264         sstrncpy (vl.plugin_instance, db->database, sizeof (vl.plugin_instance));
265
266         sstrncpy (vl.type, type, sizeof (vl.type));
267
268         if (NULL != type_instance)
269                 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
270
271         plugin_dispatch_values (&vl);
272         return;
273 } /* submit */
274
275 static void submit_counter (const c_psql_database_t *db,
276                 const char *type, const char *type_instance,
277                 const char *value)
278 {
279         value_t values[1];
280
281         if ((NULL == value) || ('\0' == *value))
282                 return;
283
284         values[0].counter = atoll (value);
285         submit (db, type, type_instance, values, 1);
286         return;
287 } /* submit_counter */
288
289 static void submit_gauge (const c_psql_database_t *db,
290                 const char *type, const char *type_instance,
291                 const char *value)
292 {
293         value_t values[1];
294
295         if ((NULL == value) || ('\0' == *value))
296                 return;
297
298         values[0].gauge = atof (value);
299         submit (db, type, type_instance, values, 1);
300         return;
301 } /* submit_gauge */
302
303 static int c_psql_check_connection (c_psql_database_t *db)
304 {
305         /* "ping" */
306         PQclear (PQexec (db->conn, "SELECT 42;"));
307
308         if (CONNECTION_OK != PQstatus (db->conn)) {
309                 PQreset (db->conn);
310
311                 /* trigger c_release() */
312                 if (0 == db->conn_complaint.interval)
313                         db->conn_complaint.interval = 1;
314
315                 if (CONNECTION_OK != PQstatus (db->conn)) {
316                         c_complain (LOG_ERR, &db->conn_complaint,
317                                         "Failed to connect to database %s: %s",
318                                         db->database, PQerrorMessage (db->conn));
319                         return -1;
320                 }
321         }
322
323         c_release (LOG_INFO, &db->conn_complaint,
324                         "Successfully reconnected to database %s", PQdb (db->conn));
325         return 0;
326 } /* c_psql_check_connection */
327
328 static int c_psql_exec_query (c_psql_database_t *db, int idx)
329 {
330         c_psql_query_t *query;
331         PGresult       *res;
332
333         char *params[db->max_params_num];
334
335         int rows, cols;
336         int i;
337
338         if (idx >= db->queries_num)
339                 return -1;
340
341         query = db->queries[idx];
342
343         assert (db->max_params_num >= query->params_num);
344
345         for (i = 0; i < query->params_num; ++i) {
346                 switch (query->params[i]) {
347                         case C_PSQL_PARAM_HOST:
348                                 params[i] = (NULL == db->host) ? "localhost" : db->host;
349                                 break;
350                         case C_PSQL_PARAM_DB:
351                                 params[i] = db->database;
352                                 break;
353                         case C_PSQL_PARAM_USER:
354                                 params[i] = db->user;
355                                 break;
356                         default:
357                                 assert (0);
358                 }
359         }
360
361         res = PQexecParams (db->conn, query->query, query->params_num, NULL,
362                         (const char *const *)((0 == query->params_num) ? NULL : params),
363                         NULL, NULL, /* return text data */ 0);
364
365         if (PGRES_TUPLES_OK != PQresultStatus (res)) {
366                 log_err ("Failed to execute SQL query: %s",
367                                 PQerrorMessage (db->conn));
368                 log_info ("SQL query was: %s", query->query);
369                 PQclear (res);
370                 return -1;
371         }
372
373         rows = PQntuples (res);
374         if (1 > rows)
375                 return 0;
376
377         cols = PQnfields (res);
378         if (query->cols_num != cols) {
379                 log_err ("SQL query returned wrong number of fields "
380                                 "(expected: %i, got: %i)", query->cols_num, cols);
381                 log_info ("SQL query was: %s", query->query);
382                 return -1;
383         }
384
385         for (i = 0; i < rows; ++i) {
386                 int j;
387
388                 for (j = 0; j < cols; ++j) {
389                         c_psql_col_t col = query->cols[j];
390
391                         char *value = PQgetvalue (res, i, j);
392
393                         if (col.ds_type == DS_TYPE_COUNTER)
394                                 submit_counter (db, col.type, col.type_instance, value);
395                         else if (col.ds_type == DS_TYPE_GAUGE)
396                                 submit_gauge (db, col.type, col.type_instance, value);
397                 }
398         }
399         return 0;
400 } /* c_psql_exec_query */
401
402 static int c_psql_stat_database (c_psql_database_t *db)
403 {
404         const char *const query =
405                 "SELECT numbackends, xact_commit, xact_rollback "
406                         "FROM pg_stat_database "
407                         "WHERE datname = $1;";
408
409         PGresult *res;
410
411         int n;
412
413         res = PQexecParams (db->conn, query, /* number of parameters */ 1,
414                         NULL, (const char *const *)&db->database, NULL, NULL,
415                         /* return text data */ 0);
416
417         if (PGRES_TUPLES_OK != PQresultStatus (res)) {
418                 log_err ("Failed to execute SQL query: %s",
419                                 PQerrorMessage (db->conn));
420                 log_info ("SQL query was: %s", query);
421                 PQclear (res);
422                 return -1;
423         }
424
425         n = PQntuples (res);
426         if (1 < n) {
427                 log_warn ("pg_stat_database has more than one entry "
428                                 "for database %s - ignoring additional results.",
429                                 db->database);
430         }
431         else if (1 > n) {
432                 log_err ("pg_stat_database has no entry for database %s",
433                                 db->database);
434                 PQclear (res);
435                 return -1;
436         }
437
438         submit_gauge (db, "pg_numbackends", NULL,  PQgetvalue (res, 0, 0));
439
440         submit_counter (db, "pg_xact", "commit",   PQgetvalue (res, 0, 1));
441         submit_counter (db, "pg_xact", "rollback", PQgetvalue (res, 0, 2));
442
443         PQclear (res);
444         return 0;
445 } /* c_psql_stat_database */
446
447 static int c_psql_read (void)
448 {
449         int success = 0;
450         int i;
451
452         for (i = 0; i < databases_num; ++i) {
453                 c_psql_database_t *db = databases + i;
454
455                 int j;
456
457                 assert (NULL != db->database);
458
459                 if (0 != c_psql_check_connection (db))
460                         continue;
461
462                 c_psql_stat_database (db);
463
464                 for (j = 0; j < db->queries_num; ++j)
465                         c_psql_exec_query (db, j);
466
467                 ++success;
468         }
469
470         if (! success)
471                 return -1;
472         return 0;
473 } /* c_psql_read */
474
475 static int c_psql_shutdown (void)
476 {
477         int i;
478
479         if ((NULL == databases) || (0 == databases_num))
480                 return 0;
481
482         plugin_unregister_read ("postgresql");
483         plugin_unregister_shutdown ("postgresql");
484
485         for (i = 0; i < databases_num; ++i) {
486                 c_psql_database_t *db = databases + i;
487                 c_psql_database_delete (db);
488         }
489
490         sfree (databases);
491         databases_num = 0;
492
493         for (i = 0; i < queries_num; ++i) {
494                 c_psql_query_t *query = queries + i;
495                 c_psql_query_delete (query);
496         }
497
498         sfree (queries);
499         queries_num = 0;
500         return 0;
501 } /* c_psql_shutdown */
502
503 static int c_psql_init (void)
504 {
505         int i;
506
507         if ((NULL == databases) || (0 == databases_num))
508                 return 0;
509
510         for (i = 0; i < queries_num; ++i) {
511                 c_psql_query_t *query = queries + i;
512                 int j;
513
514                 for (j = 0; j < query->cols_num; ++j) {
515                         c_psql_col_t     *col = query->cols + j;
516                         const data_set_t *ds;
517
518                         ds = plugin_get_ds (col->type);
519                         if (NULL == ds) {
520                                 log_err ("Column: Unknown type \"%s\".", col->type);
521                                 c_psql_shutdown ();
522                                 return -1;
523                         }
524
525                         if (1 != ds->ds_num) {
526                                 log_err ("Column: Invalid type \"%s\" - types defining "
527                                                 "one data source are supported only (got: %i).",
528                                                 col->type, ds->ds_num);
529                                 c_psql_shutdown ();
530                                 return -1;
531                         }
532
533                         col->ds_type = ds->ds[0].type;
534                 }
535         }
536
537         for (i = 0; i < databases_num; ++i) {
538                 c_psql_database_t *db = databases + i;
539
540                 char  conninfo[4096];
541                 char *buf     = conninfo;
542                 int   buf_len = sizeof (conninfo);
543                 int   status;
544
545                 char *server_host;
546                 int   server_version;
547
548                 status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
549                 if (0 < status) {
550                         buf     += status;
551                         buf_len -= status;
552                 }
553
554                 C_PSQL_PAR_APPEND (buf, buf_len, "host",       db->host);
555                 C_PSQL_PAR_APPEND (buf, buf_len, "port",       db->port);
556                 C_PSQL_PAR_APPEND (buf, buf_len, "user",       db->user);
557                 C_PSQL_PAR_APPEND (buf, buf_len, "password",   db->password);
558                 C_PSQL_PAR_APPEND (buf, buf_len, "sslmode",    db->sslmode);
559                 C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
560                 C_PSQL_PAR_APPEND (buf, buf_len, "service",    db->service);
561
562                 db->conn = PQconnectdb (conninfo);
563                 if (0 != c_psql_check_connection (db))
564                         continue;
565
566                 server_host    = PQhost (db->conn);
567                 server_version = PQserverVersion (db->conn);
568                 log_info ("Sucessfully connected to database %s (user %s) "
569                                 "at server %s%s%s (server version: %d.%d.%d, "
570                                 "protocol version: %d, pid: %d)",
571                                 PQdb (db->conn), PQuser (db->conn),
572                                 C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
573                                 C_PSQL_SERVER_VERSION3 (server_version),
574                                 PQprotocolVersion (db->conn), PQbackendPID (db->conn));
575         }
576
577         plugin_register_read ("postgresql", c_psql_read);
578         plugin_register_shutdown ("postgresql", c_psql_shutdown);
579         return 0;
580 } /* c_psql_init */
581
582 static int config_set (char *name, char **var, const oconfig_item_t *ci)
583 {
584         if ((0 != ci->children_num) || (1 != ci->values_num)
585                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
586                 log_err ("%s expects a single string argument.", name);
587                 return 1;
588         }
589
590         sfree (*var);
591         *var = sstrdup (ci->values[0].value.string);
592         return 0;
593 } /* config_set */
594
595 static int config_set_param (c_psql_query_t *query, const oconfig_item_t *ci)
596 {
597         c_psql_param_t param;
598         char          *param_str;
599
600         if ((0 != ci->children_num) || (1 != ci->values_num)
601                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
602                 log_err ("Param expects a single string argument.");
603                 return 1;
604         }
605
606         param_str = ci->values[0].value.string;
607         if (0 == strcasecmp (param_str, "hostname"))
608                 param = C_PSQL_PARAM_HOST;
609         else if (0 == strcasecmp (param_str, "database"))
610                 param = C_PSQL_PARAM_DB;
611         else if (0 == strcasecmp (param_str, "username"))
612                 param = C_PSQL_PARAM_USER;
613         else {
614                 log_err ("Invalid parameter \"%s\".", param_str);
615                 return 1;
616         }
617
618         ++query->params_num;
619         if (NULL == (query->params = (c_psql_param_t *)realloc (query->params,
620                                 query->params_num * sizeof (*query->params)))) {
621                 log_err ("Out of memory.");
622                 exit (5);
623         }
624
625         query->params[query->params_num - 1] = param;
626         return 0;
627 } /* config_set_param */
628
629 static int config_set_column (c_psql_query_t *query, const oconfig_item_t *ci)
630 {
631         c_psql_col_t *col;
632
633         int i;
634
635         if ((0 != ci->children_num)
636                         || (1 > ci->values_num) || (2 < ci->values_num)) {
637                 log_err ("Column expects either one or two arguments.");
638                 return 1;
639         }
640
641         for (i = 0; i < ci->values_num; ++i) {
642                 if (OCONFIG_TYPE_STRING != ci->values[i].type) {
643                         log_err ("Column expects either one or two string arguments.");
644                         return 1;
645                 }
646         }
647
648         ++query->cols_num;
649         if (NULL == (query->cols = (c_psql_col_t *)realloc (query->cols,
650                                 query->cols_num * sizeof (*query->cols)))) {
651                 log_err ("Out of memory.");
652                 exit (5);
653         }
654
655         col = query->cols + query->cols_num - 1;
656
657         col->ds_type = -1;
658
659         col->type = sstrdup (ci->values[0].value.string);
660         col->type_instance = (2 == ci->values_num)
661                 ? sstrdup (ci->values[1].value.string) : NULL;
662         return 0;
663 } /* config_set_column */
664
665 static int config_set_query (c_psql_database_t *db, const oconfig_item_t *ci)
666 {
667         c_psql_query_t *query;
668
669         if ((0 != ci->children_num) || (1 != ci->values_num)
670                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
671                 log_err ("Query expects a single string argument.");
672                 return 1;
673         }
674
675         query = c_psql_query_get (ci->values[0].value.string);
676         if (NULL == query) {
677                 log_err ("Query \"%s\" not found - please check your configuration.",
678                                 ci->values[0].value.string);
679                 return 1;
680         }
681
682         ++db->queries_num;
683         if (NULL == (db->queries = (c_psql_query_t **)realloc (db->queries,
684                                 db->queries_num * sizeof (*db->queries)))) {
685                 log_err ("Out of memory.");
686                 exit (5);
687         }
688
689         if (query->params_num > db->max_params_num)
690                 db->max_params_num = query->params_num;
691
692         db->queries[db->queries_num - 1] = query;
693         return 0;
694 } /* config_set_query */
695
696 static int c_psql_config_query (oconfig_item_t *ci)
697 {
698         c_psql_query_t *query;
699
700         int i;
701
702         if ((1 != ci->values_num)
703                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
704                 log_err ("<Query> expects a single string argument.");
705                 return 1;
706         }
707
708         query = c_psql_query_new (ci->values[0].value.string);
709
710         for (i = 0; i < ci->children_num; ++i) {
711                 oconfig_item_t *c = ci->children + i;
712
713                 if (0 == strcasecmp (c->key, "Query"))
714                         config_set ("Query", &query->query, c);
715                 else if (0 == strcasecmp (c->key, "Param"))
716                         config_set_param (query, c);
717                 else if (0 == strcasecmp (c->key, "Column"))
718                         config_set_column (query, c);
719                 else
720                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
721         }
722         return 0;
723 } /* c_psql_config_query */
724
725 static int c_psql_config_database (oconfig_item_t *ci)
726 {
727         c_psql_database_t *db;
728
729         int i;
730
731         if ((1 != ci->values_num)
732                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
733                 log_err ("<Database> expects a single string argument.");
734                 return 1;
735         }
736
737         db = c_psql_database_new (ci->values[0].value.string);
738
739         for (i = 0; i < ci->children_num; ++i) {
740                 oconfig_item_t *c = ci->children + i;
741
742                 if (0 == strcasecmp (c->key, "Host"))
743                         config_set ("Host", &db->host, c);
744                 else if (0 == strcasecmp (c->key, "Port"))
745                         config_set ("Port", &db->port, c);
746                 else if (0 == strcasecmp (c->key, "User"))
747                         config_set ("User", &db->user, c);
748                 else if (0 == strcasecmp (c->key, "Password"))
749                         config_set ("Password", &db->password, c);
750                 else if (0 == strcasecmp (c->key, "SSLMode"))
751                         config_set ("SSLMode", &db->sslmode, c);
752                 else if (0 == strcasecmp (c->key, "KRBSrvName"))
753                         config_set ("KRBSrvName", &db->krbsrvname, c);
754                 else if (0 == strcasecmp (c->key, "Service"))
755                         config_set ("Service", &db->service, c);
756                 else if (0 == strcasecmp (c->key, "Query"))
757                         config_set_query (db, c);
758                 else
759                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
760         }
761
762         if (NULL == db->queries) {
763                 db->queries = (c_psql_query_t **)malloc (def_queries_num
764                                 * sizeof (*db->queries));
765
766                 for (i = 0; i < def_queries_num; ++i) {
767                         db->queries[i] = c_psql_query_get (def_queries[i]);
768                         if (NULL == db->queries[i])
769                                 log_err ("Query \"%s\" not found - "
770                                                 "please check your installation.",
771                                                 def_queries[i]);
772                         else
773                                 ++db->queries_num;
774                 }
775         }
776         return 0;
777 }
778
779 static int c_psql_config (oconfig_item_t *ci)
780 {
781         static int have_def_config = 0;
782
783         int i;
784
785         if (0 == have_def_config) {
786                 oconfig_item_t *c;
787
788                 have_def_config = 1;
789
790                 c = oconfig_parse_file (C_PSQL_DEFAULT_CONF);
791                 if (NULL == c)
792                         log_err ("Failed to read default config ("C_PSQL_DEFAULT_CONF").");
793                 else
794                         c_psql_config (c);
795
796                 if (NULL == queries)
797                         log_err ("Default config ("C_PSQL_DEFAULT_CONF") did not define "
798                                         "any queries - please check your installation.");
799         }
800
801         for (i = 0; i < ci->children_num; ++i) {
802                 oconfig_item_t *c = ci->children + i;
803
804                 if (0 == strcasecmp (c->key, "Query"))
805                         c_psql_config_query (c);
806                 else if (0 == strcasecmp (c->key, "Database"))
807                         c_psql_config_database (c);
808                 else
809                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
810         }
811         return 0;
812 } /* c_psql_config */
813
814 void module_register (void)
815 {
816         plugin_register_complex_config ("postgresql", c_psql_config);
817         plugin_register_init ("postgresql", c_psql_init);
818 } /* module_register */
819
820 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
821