Merge branch 'collectd-4.5' into collectd-4.6
[collectd.git] / src / utils_db_query.c
1 /**
2  * collectd - src/utils_db_query.c
3  * Copyright (C) 2008,2009  Florian octo Forster
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  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
26 #include "utils_db_query.h"
27
28 /*
29  * Data types
30  */
31 struct udb_result_s; /* {{{ */
32 typedef struct udb_result_s udb_result_t;
33 struct udb_result_s
34 {
35   char    *type;
36   char    *instance_prefix;
37   char   **instances;
38   size_t   instances_num;
39   char   **values;
40   size_t   values_num;
41
42   /* Preparation area */
43   const   data_set_t *ds;
44   size_t *instances_pos;
45   size_t *values_pos;
46   char  **instances_buffer;
47   char  **values_buffer;
48
49   /* Legacy data */
50   int legacy_mode;
51   size_t legacy_position;
52   /* When in legacy mode:
53    * - type/ds hold the format of the data
54    * - instance_prefix is used as type-instance if non-NULL
55    * - legacy_position holds the index of the column to use as value.
56    */
57
58   udb_result_t *next;
59 }; /* }}} */
60
61 struct udb_query_s /* {{{ */
62 {
63   char *name;
64   char *statement;
65   void *user_data;
66
67   int legacy_mode;
68
69   unsigned int min_version;
70   unsigned int max_version;
71
72   /* Preparation area */
73   size_t column_num;
74   char *host;
75   char *plugin;
76   char *db_name;
77
78   udb_result_t *results;
79 }; /* }}} */
80
81 /*
82  * Config Private functions
83  */
84 static int udb_config_set_string (char **ret_string, /* {{{ */
85     oconfig_item_t *ci)
86 {
87   char *string;
88
89   if ((ci->values_num != 1)
90       || (ci->values[0].type != OCONFIG_TYPE_STRING))
91   {
92     WARNING ("db query utils: The `%s' config option "
93         "needs exactly one string argument.", ci->key);
94     return (-1);
95   }
96
97   string = strdup (ci->values[0].value.string);
98   if (string == NULL)
99   {
100     ERROR ("db query utils: strdup failed.");
101     return (-1);
102   }
103
104   if (*ret_string != NULL)
105     free (*ret_string);
106   *ret_string = string;
107
108   return (0);
109 } /* }}} int udb_config_set_string */
110
111 static int udb_config_add_string (char ***ret_array, /* {{{ */
112     size_t *ret_array_len, oconfig_item_t *ci)
113 {
114   char **array;
115   size_t array_len;
116   int i;
117
118   if (ci->values_num < 1)
119   {
120     WARNING ("db query utils: The `%s' config option "
121         "needs at least one argument.", ci->key);
122     return (-1);
123   }
124
125   for (i = 0; i < ci->values_num; i++)
126   {
127     if (ci->values[i].type != OCONFIG_TYPE_STRING)
128     {
129       WARNING ("db query utils: Argument %i to the `%s' option "
130           "is not a string.", i + 1, ci->key);
131       return (-1);
132     }
133   }
134
135   array_len = *ret_array_len;
136   array = (char **) realloc (*ret_array,
137       sizeof (char *) * (array_len + ci->values_num));
138   if (array == NULL)
139   {
140     ERROR ("db query utils: realloc failed.");
141     return (-1);
142   }
143   *ret_array = array;
144
145   for (i = 0; i < ci->values_num; i++)
146   {
147     array[array_len] = strdup (ci->values[i].value.string);
148     if (array[array_len] == NULL)
149     {
150       ERROR ("db query utils: strdup failed.");
151       *ret_array_len = array_len;
152       return (-1);
153     }
154     array_len++;
155   }
156
157   *ret_array_len = array_len;
158   return (0);
159 } /* }}} int udb_config_add_string */
160
161 static int udb_config_set_uint (unsigned int *ret_value, /* {{{ */
162     oconfig_item_t *ci)
163 {
164   double tmp;
165
166   if ((ci->values_num != 1)
167       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
168   {
169     WARNING ("db query utils: The `%s' config option "
170         "needs exactly one numeric argument.", ci->key);
171     return (-1);
172   }
173
174   tmp = ci->values[0].value.number;
175   if ((tmp < 0.0) || (tmp > ((double) UINT_MAX)))
176     return (-ERANGE);
177
178   *ret_value = (unsigned int) (tmp + .5);
179   return (0);
180 } /* }}} int udb_config_set_uint */
181
182 /*
183  * Legacy result private functions
184  */
185 static void udb_legacy_result_finish_result (udb_result_t *r) /* {{{ */
186 {
187   if (r == NULL)
188     return;
189
190   assert (r->legacy_mode == 1);
191
192   r->ds = NULL;
193 } /* }}} void udb_legacy_result_finish_result */
194
195 static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
196     udb_query_t *q, char **column_values)
197 {
198   value_list_t vl = VALUE_LIST_INIT;
199   value_t value;
200   char *endptr;
201
202   assert (r->legacy_mode == 1);
203   assert (r->ds != NULL);
204   assert (r->ds->ds_num == 1);
205
206   vl.values = &value;
207   vl.values_len = 1;
208
209   endptr = NULL;
210   errno = 0;
211   if (r->ds->ds[0].type == DS_TYPE_COUNTER)
212     vl.values[0].counter = (counter_t) strtoll (column_values[r->legacy_position],
213         &endptr, /* base = */ 0);
214   else if (r->ds->ds[0].type == DS_TYPE_GAUGE)
215     vl.values[0].gauge = (gauge_t) strtod (column_values[r->legacy_position],
216         &endptr);
217   else
218     errno = EINVAL;
219
220   if ((endptr == column_values[r->legacy_position]) || (errno != 0))
221   {
222     WARNING ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
223         column_values[r->legacy_position],
224         (r->ds->ds[0].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
225     vl.values[0].gauge = NAN;
226   }
227
228   sstrncpy (vl.host, q->host, sizeof (vl.host));
229   sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin));
230   sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance));
231   sstrncpy (vl.type, r->type, sizeof (vl.type));
232
233   if (r->instance_prefix != NULL)
234     sstrncpy (vl.type_instance, r->instance_prefix,
235         sizeof (vl.type_instance));
236
237   plugin_dispatch_values (&vl);
238
239   return (0);
240 } /* }}} int udb_legacy_result_handle_result */
241
242 static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
243     char **column_names, size_t column_num)
244 {
245   if (r == NULL)
246     return (-EINVAL);
247
248   assert (r->legacy_mode == 1);
249
250   /* Make sure previous preparations are cleaned up. */
251   udb_legacy_result_finish_result (r);
252
253   if (r->legacy_position >= column_num)
254   {
255     ERROR ("db query utils: The legacy configuration specified (at least) "
256         "%zu `Column's, but the query returned only %zu columns!",
257         r->legacy_position + 1, column_num);
258     return (-ENOENT);
259   }
260
261   /* Read `ds' and check number of values {{{ */
262   r->ds = plugin_get_ds (r->type);
263   if (r->ds == NULL)
264   {
265     ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
266         "known by the daemon. See types.db(5) for details.",
267         r->type);
268     return (-1);
269   }
270
271   if (r->ds->ds_num != 1)
272   {
273     ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
274         "requires exactly %i values, but the legacy configuration "
275         "requires exactly one!",
276         r->type,
277         r->ds->ds_num);
278     return (-1);
279   }
280   /* }}} */
281
282   return (0);
283 } /* }}} int udb_legacy_result_prepare_result */
284
285 static int udb_legacy_result_create (const char *query_name, /* {{{ */
286     udb_result_t **r_head, oconfig_item_t *ci, size_t position)
287 {
288   udb_result_t *r;
289
290   if ((ci->values_num < 1) || (ci->values_num > 2)
291       || (ci->values[0].type != OCONFIG_TYPE_STRING)
292       || ((ci->values_num == 2)
293         && (ci->values[1].type != OCONFIG_TYPE_STRING)))
294   {
295     WARNING ("db query utils: The `Column' block needs either one or two "
296         "string arguments.");
297     return (-1);
298   }
299
300   r = (udb_result_t *) malloc (sizeof (*r));
301   if (r == NULL)
302   {
303     ERROR ("db query utils: malloc failed.");
304     return (-1);
305   }
306   memset (r, 0, sizeof (*r));
307
308   r->legacy_mode = 1;
309   r->legacy_position = position;
310
311   r->type = strdup (ci->values[0].value.string);
312   if (r->type == NULL)
313   {
314     ERROR ("db query utils: strdup failed.");
315     free (r);
316     return (-1);
317   }
318
319   r->instance_prefix = NULL;
320   if (ci->values_num == 2)
321   {
322     r->instance_prefix = strdup (ci->values[1].value.string);
323     if (r->instance_prefix == NULL)
324     {
325       ERROR ("db query utils: strdup failed.");
326       free (r->type);
327       free (r);
328       return (-1);
329     }
330   }
331
332   /* If all went well, add this result to the list of results. */
333   if (*r_head == NULL)
334   {
335     *r_head = r;
336   }
337   else
338   {
339     udb_result_t *last;
340
341     last = *r_head;
342     while (last->next != NULL)
343       last = last->next;
344
345     last->next = r;
346   }
347
348   return (0);
349 } /* }}} int udb_legacy_result_create */
350
351 /*
352  * Result private functions
353  */
354 static void udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
355 {
356   value_list_t vl = VALUE_LIST_INIT;
357   size_t i;
358
359   assert (r != NULL);
360   assert (r->legacy_mode == 0);
361   assert (r->ds != NULL);
362   assert (((size_t) r->ds->ds_num) == r->values_num);
363
364   vl.values = (value_t *) calloc (r->ds->ds_num, sizeof (value_t));
365   if (vl.values == NULL)
366   {
367     ERROR ("db query utils: malloc failed.");
368     return;
369   }
370   vl.values_len = r->ds->ds_num;
371
372   for (i = 0; i < r->values_num; i++)
373   {
374     char *endptr;
375
376     endptr = NULL;
377     errno = 0;
378     if (r->ds->ds[i].type == DS_TYPE_COUNTER)
379       vl.values[i].counter = (counter_t) strtoll (r->values_buffer[i],
380           &endptr, /* base = */ 0);
381     else if (r->ds->ds[i].type == DS_TYPE_GAUGE)
382       vl.values[i].gauge = (gauge_t) strtod (r->values_buffer[i], &endptr);
383     else
384       errno = EINVAL;
385
386     if ((endptr == r->values_buffer[i]) || (errno != 0))
387     {
388       WARNING ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
389           r->values_buffer[i],
390           (r->ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
391       vl.values[i].gauge = NAN;
392     }
393   }
394
395   sstrncpy (vl.host, q->host, sizeof (vl.host));
396   sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin));
397   sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance));
398   sstrncpy (vl.type, r->type, sizeof (vl.type));
399
400   /* Set vl.type_instance {{{ */
401   if (r->instances_num <= 0)
402   {
403     if (r->instance_prefix == NULL)
404       vl.type_instance[0] = 0;
405     else
406       sstrncpy (vl.type_instance, r->instance_prefix,
407           sizeof (vl.type_instance));
408   }
409   else /* if ((r->instances_num > 0) */
410   {
411     if (r->instance_prefix == NULL)
412     {
413       strjoin (vl.type_instance, sizeof (vl.type_instance),
414           r->instances_buffer, r->instances_num, "-");
415     }
416     else
417     {
418       char tmp[DATA_MAX_NAME_LEN];
419
420       strjoin (tmp, sizeof (tmp), r->instances_buffer, r->instances_num, "-");
421       tmp[sizeof (tmp) - 1] = 0;
422
423       snprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
424           r->instance_prefix, tmp);
425     }
426   }
427   vl.type_instance[sizeof (vl.type_instance) - 1] = 0;
428   /* }}} */
429
430   plugin_dispatch_values (&vl);
431
432   sfree (vl.values);
433 } /* }}} void udb_result_submit */
434
435 static void udb_result_finish_result (udb_result_t *r) /* {{{ */
436 {
437   if (r == NULL)
438     return;
439
440   if (r->legacy_mode == 1)
441   {
442     udb_legacy_result_finish_result (r);
443     return;
444   }
445
446   assert (r->legacy_mode == 0);
447
448   r->ds = NULL;
449   sfree (r->instances_pos);
450   sfree (r->values_pos);
451   sfree (r->instances_buffer);
452   sfree (r->values_buffer);
453 } /* }}} void udb_result_finish_result */
454
455 static int udb_result_handle_result (udb_result_t *r, /* {{{ */
456     udb_query_t *q, char **column_values)
457 {
458   size_t i;
459
460   if (r->legacy_mode == 1)
461     return (udb_legacy_result_handle_result (r, q, column_values));
462
463   assert (r->legacy_mode == 0);
464
465   for (i = 0; i < r->instances_num; i++)
466     r->instances_buffer[i] = column_values[r->instances_pos[i]];
467
468   for (i = 0; i < r->values_num; i++)
469     r->values_buffer[i] = column_values[r->values_pos[i]];
470
471   udb_result_submit (r, q);
472
473   return (0);
474 } /* }}} int udb_result_handle_result */
475
476 static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
477     char **column_names, size_t column_num)
478 {
479   size_t i;
480
481   if (r == NULL)
482     return (-EINVAL);
483
484   if (r->legacy_mode == 1)
485     return (udb_legacy_result_prepare_result (r, column_names, column_num));
486
487   assert (r->legacy_mode == 0);
488
489 #define BAIL_OUT(status) \
490   r->ds = NULL; \
491   sfree (r->instances_pos); \
492   sfree (r->values_pos); \
493   sfree (r->instances_buffer); \
494   sfree (r->values_buffer); \
495   return (status)
496
497   /* Make sure previous preparations are cleaned up. */
498   udb_result_finish_result (r);
499   r->instances_pos = NULL;
500   r->values_pos = NULL;
501
502   /* Read `ds' and check number of values {{{ */
503   r->ds = plugin_get_ds (r->type);
504   if (r->ds == NULL)
505   {
506     ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
507         "known by the daemon. See types.db(5) for details.",
508         r->type);
509     BAIL_OUT (-1);
510   }
511
512   if (((size_t) r->ds->ds_num) != r->values_num)
513   {
514     ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
515         "requires exactly %i value%s, but the configuration specifies %zu.",
516         r->type,
517         r->ds->ds_num, (r->ds->ds_num == 1) ? "" : "s",
518         r->values_num);
519     BAIL_OUT (-1);
520   }
521   /* }}} */
522
523   /* Allocate r->instances_pos, r->values_pos, r->instances_buffer, and
524    * r->values_buffer {{{ */
525   if (r->instances_num > 0)
526   {
527     r->instances_pos = (size_t *) calloc (r->instances_num, sizeof (size_t));
528     if (r->instances_pos == NULL)
529     {
530       ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
531       BAIL_OUT (-ENOMEM);
532     }
533
534     r->instances_buffer = (char **) calloc (r->instances_num, sizeof (char *));
535     if (r->instances_buffer == NULL)
536     {
537       ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
538       BAIL_OUT (-ENOMEM);
539     }
540   } /* if (r->instances_num > 0) */
541
542   r->values_pos = (size_t *) calloc (r->values_num, sizeof (size_t));
543   if (r->values_pos == NULL)
544   {
545     ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
546     BAIL_OUT (-ENOMEM);
547   }
548
549   r->values_buffer = (char **) calloc (r->values_num, sizeof (char *));
550   if (r->values_buffer == NULL)
551   {
552     ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
553     BAIL_OUT (-ENOMEM);
554   }
555   /* }}} */
556
557   /* Determine the position of the instance columns {{{ */
558   for (i = 0; i < r->instances_num; i++)
559   {
560     size_t j;
561
562     for (j = 0; j < column_num; j++)
563     {
564       if (strcasecmp (r->instances[i], column_names[j]) == 0)
565       {
566         r->instances_pos[i] = j;
567         break;
568       }
569     }
570
571     if (j >= column_num)
572     {
573       ERROR ("db query utils: udb_result_prepare_result: "
574           "Column `%s' could not be found.",
575           r->instances[i]);
576       BAIL_OUT (-ENOENT);
577     }
578   } /* }}} for (i = 0; i < r->instances_num; i++) */
579
580   /* Determine the position of the value columns {{{ */
581   for (i = 0; i < r->values_num; i++)
582   {
583     size_t j;
584
585     for (j = 0; j < column_num; j++)
586     {
587       if (strcasecmp (r->values[i], column_names[j]) == 0)
588       {
589         r->values_pos[i] = j;
590         break;
591       }
592     }
593
594     if (j >= column_num)
595     {
596       ERROR ("db query utils: udb_result_prepare_result: "
597           "Column `%s' could not be found.",
598           r->values[i]);
599       BAIL_OUT (-ENOENT);
600     }
601   } /* }}} for (i = 0; i < r->values_num; i++) */
602
603 #undef BAIL_OUT
604   return (0);
605 } /* }}} int udb_result_prepare_result */
606
607 static void udb_result_free (udb_result_t *r) /* {{{ */
608 {
609   size_t i;
610
611   if (r == NULL)
612     return;
613
614   sfree (r->type);
615
616   for (i = 0; i < r->instances_num; i++)
617     sfree (r->instances[i]);
618   sfree (r->instances);
619
620   for (i = 0; i < r->values_num; i++)
621     sfree (r->values[i]);
622   sfree (r->values);
623
624   udb_result_free (r->next);
625
626   sfree (r);
627 } /* }}} void udb_result_free */
628
629 static int udb_result_create (const char *query_name, /* {{{ */
630     udb_result_t **r_head, oconfig_item_t *ci)
631 {
632   udb_result_t *r;
633   int status;
634   int i;
635
636   if (ci->values_num != 0)
637   {
638     WARNING ("db query utils: The `Result' block doesn't accept "
639         "any arguments. Ignoring %i argument%s.",
640         ci->values_num, (ci->values_num == 1) ? "" : "s");
641   }
642
643   r = (udb_result_t *) malloc (sizeof (*r));
644   if (r == NULL)
645   {
646     ERROR ("db query utils: malloc failed.");
647     return (-1);
648   }
649   memset (r, 0, sizeof (*r));
650   r->type = NULL;
651   r->instance_prefix = NULL;
652   r->instances = NULL;
653   r->values = NULL;
654   r->next = NULL;
655
656   /* Fill the `udb_result_t' structure.. */
657   status = 0;
658   for (i = 0; i < ci->children_num; i++)
659   {
660     oconfig_item_t *child = ci->children + i;
661
662     if (strcasecmp ("Type", child->key) == 0)
663       status = udb_config_set_string (&r->type, child);
664     else if (strcasecmp ("InstancePrefix", child->key) == 0)
665       status = udb_config_set_string (&r->instance_prefix, child);
666     else if (strcasecmp ("InstancesFrom", child->key) == 0)
667       status = udb_config_add_string (&r->instances, &r->instances_num, child);
668     else if (strcasecmp ("ValuesFrom", child->key) == 0)
669       status = udb_config_add_string (&r->values, &r->values_num, child);
670     else
671     {
672       WARNING ("db query utils: Query `%s': Option `%s' not allowed here.",
673           query_name, child->key);
674       status = -1;
675     }
676
677     if (status != 0)
678       break;
679   }
680
681   /* Check that all necessary options have been given. */
682   while (status == 0)
683   {
684     if (r->type == NULL)
685     {
686       WARNING ("db query utils: `Type' not given for "
687           "result in query `%s'", query_name);
688       status = -1;
689     }
690     if (r->values == NULL)
691     {
692       WARNING ("db query utils: `ValuesFrom' not given for "
693           "result in query `%s'", query_name);
694       status = -1;
695     }
696
697     break;
698   } /* while (status == 0) */
699
700   if (status != 0)
701   {
702     udb_result_free (r);
703     return (-1);
704   }
705
706   /* If all went well, add this result to the list of results. */
707   if (*r_head == NULL)
708   {
709     *r_head = r;
710   }
711   else
712   {
713     udb_result_t *last;
714
715     last = *r_head;
716     while (last->next != NULL)
717       last = last->next;
718
719     last->next = r;
720   }
721
722   return (0);
723 } /* }}} int udb_result_create */
724
725 /*
726  * Query private functions
727  */
728 void udb_query_free_one (udb_query_t *q) /* {{{ */
729 {
730   if (q == NULL)
731     return;
732
733   sfree (q->name);
734   sfree (q->statement);
735
736   udb_result_free (q->results);
737
738   sfree (q);
739 } /* }}} void udb_query_free_one */
740
741 /*
742  * Query public functions
743  */
744 int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */
745     size_t *ret_query_list_len, oconfig_item_t *ci,
746     udb_query_create_callback_t cb, int legacy_mode)
747 {
748   udb_query_t **query_list;
749   size_t        query_list_len;
750
751   udb_query_t *q;
752   int status;
753   int i;
754
755   size_t legacy_position;
756
757   if ((ret_query_list == NULL) || (ret_query_list_len == NULL))
758     return (-EINVAL);
759   query_list     = *ret_query_list;
760   query_list_len = *ret_query_list_len;
761
762   if ((ci->values_num != 1)
763       || (ci->values[0].type != OCONFIG_TYPE_STRING))
764   {
765     WARNING ("db query utils: The `Query' block "
766         "needs exactly one string argument.");
767     return (-1);
768   }
769
770   q = (udb_query_t *) malloc (sizeof (*q));
771   if (q == NULL)
772   {
773     ERROR ("db query utils: malloc failed.");
774     return (-1);
775   }
776   memset (q, 0, sizeof (*q));
777   q->legacy_mode = legacy_mode;
778   q->min_version = 0;
779   q->max_version = UINT_MAX;
780
781   legacy_position = 0;
782
783   status = udb_config_set_string (&q->name, ci);
784   if (status != 0)
785   {
786     sfree (q);
787     return (status);
788   }
789
790   /* Fill the `udb_query_t' structure.. */
791   for (i = 0; i < ci->children_num; i++)
792   {
793     oconfig_item_t *child = ci->children + i;
794
795     if (strcasecmp ("Statement", child->key) == 0)
796       status = udb_config_set_string (&q->statement, child);
797     else if (strcasecmp ("Result", child->key) == 0)
798       status = udb_result_create (q->name, &q->results, child);
799     else if (strcasecmp ("MinVersion", child->key) == 0)
800       status = udb_config_set_uint (&q->min_version, child);
801     else if (strcasecmp ("MaxVersion", child->key) == 0)
802       status = udb_config_set_uint (&q->max_version, child);
803
804     /* PostgreSQL compatibility code */
805     else if ((strcasecmp ("Query", child->key) == 0)
806         && (q->legacy_mode == 1))
807     {
808       WARNING ("db query utils: Query `%s': The `Query' option is "
809           "deprecated. Please use `Statement' instead.",
810           q->name);
811       status = udb_config_set_string (&q->statement, child);
812     }
813     else if ((strcasecmp ("Column", child->key) == 0)
814         && (q->legacy_mode == 1))
815     {
816       WARNING ("db query utils: Query `%s': The `Column' option is "
817           "deprecated. Please use the new syntax instead.",
818           q->name);
819       status = udb_legacy_result_create (q->name, &q->results, child,
820           legacy_position);
821       legacy_position++;
822     }
823     else if ((strcasecmp ("MinPGVersion", child->key) == 0)
824         && (q->legacy_mode == 1))
825     {
826       WARNING ("db query utils: Query `%s': The `MinPGVersion' option is "
827           "deprecated. Please use `MinVersion' instead.",
828           q->name);
829       status = udb_config_set_uint (&q->min_version, child);
830     }
831     else if ((strcasecmp ("MaxPGVersion", child->key) == 0)
832         && (q->legacy_mode == 1))
833     {
834       WARNING ("db query utils: Query `%s': The `MaxPGVersion' option is "
835           "deprecated. Please use `MaxVersion' instead.",
836           q->name);
837       status = udb_config_set_uint (&q->max_version, child);
838     }
839
840     /* Call custom callbacks */
841     else if (cb != NULL)
842     {
843       status = (*cb) (q, child);
844       if (status != 0)
845       {
846         WARNING ("db query utils: The configuration callback failed "
847             "to handle `%s'.", child->key);
848       }
849     }
850     else
851     {
852       WARNING ("db query utils: Query `%s': Option `%s' not allowed here.",
853           q->name, child->key);
854       status = -1;
855     }
856
857     if (status != 0)
858       break;
859   }
860
861   /* Check that all necessary options have been given. */
862   if (status == 0)
863   {
864     if (q->statement == NULL)
865     {
866       WARNING ("db query utils: Query `%s': No `Statement' given.", q->name);
867       status = -1;
868     }
869     if (q->results == NULL)
870     {
871       WARNING ("db query utils: Query `%s': No (valid) `Result' block given.",
872           q->name);
873       status = -1;
874     }
875   } /* if (status == 0) */
876
877   /* If all went well, add this query to the list of queries within the
878    * database structure. */
879   if (status == 0)
880   {
881     udb_query_t **temp;
882
883     temp = (udb_query_t **) realloc (query_list,
884         sizeof (*query_list) * (query_list_len + 1));
885     if (temp == NULL)
886     {
887       ERROR ("db query utils: realloc failed");
888       status = -1;
889     }
890     else
891     {
892       query_list = temp;
893       query_list[query_list_len] = q;
894       query_list_len++;
895     }
896   }
897
898   if (status != 0)
899   {
900     udb_query_free_one (q);
901     return (-1);
902   }
903
904   *ret_query_list     = query_list;
905   *ret_query_list_len = query_list_len;
906
907   return (0);
908 } /* }}} int udb_query_create */
909
910 void udb_query_free (udb_query_t **query_list, size_t query_list_len) /* {{{ */
911 {
912   size_t i;
913
914   if (query_list == NULL)
915     return;
916
917   for (i = 0; i < query_list_len; i++)
918     udb_query_free_one (query_list[i]);
919
920   sfree (query_list);
921 } /* }}} void udb_query_free */
922
923 int udb_query_pick_from_list_by_name (const char *name, /* {{{ */
924     udb_query_t **src_list, size_t src_list_len,
925     udb_query_t ***dst_list, size_t *dst_list_len)
926 {
927   size_t i;
928   int num_added;
929
930   if ((name == NULL) || (src_list == NULL) || (dst_list == NULL)
931       || (dst_list_len == NULL))
932   {
933     ERROR ("db query utils: udb_query_pick_from_list_by_name: "
934         "Invalid argument.");
935     return (-EINVAL);
936   }
937
938   num_added = 0;
939   for (i = 0; i < src_list_len; i++)
940   {
941     udb_query_t **tmp_list;
942     size_t tmp_list_len;
943
944     if (strcasecmp (name, src_list[i]->name) != 0)
945       continue;
946
947     tmp_list_len = *dst_list_len;
948     tmp_list = (udb_query_t **) realloc (*dst_list, (tmp_list_len + 1)
949         * sizeof (udb_query_t *));
950     if (tmp_list == NULL)
951     {
952       ERROR ("db query utils: realloc failed.");
953       return (-ENOMEM);
954     }
955
956     tmp_list[tmp_list_len] = src_list[i];
957     tmp_list_len++;
958
959     *dst_list = tmp_list;
960     *dst_list_len = tmp_list_len;
961
962     num_added++;
963   } /* for (i = 0; i < src_list_len; i++) */
964
965   if (num_added <= 0)
966   {
967     ERROR ("db query utils: Cannot find query `%s'. Make sure the <Query> "
968         "block is above the database definition!",
969         name);
970     return (-ENOENT);
971   }
972   else
973   {
974     DEBUG ("db query utils: Added %i versions of query `%s'.",
975         num_added, name);
976   }
977
978   return (0);
979 } /* }}} int udb_query_pick_from_list_by_name */
980
981 int udb_query_pick_from_list (oconfig_item_t *ci, /* {{{ */
982     udb_query_t **src_list, size_t src_list_len,
983     udb_query_t ***dst_list, size_t *dst_list_len)
984 {
985   const char *name;
986
987   if ((ci == NULL) || (src_list == NULL) || (dst_list == NULL)
988       || (dst_list_len == NULL))
989   {
990     ERROR ("db query utils: udb_query_pick_from_list: "
991         "Invalid argument.");
992     return (-EINVAL);
993   }
994
995   if ((ci->values_num != 1)
996       || (ci->values[0].type != OCONFIG_TYPE_STRING))
997   {
998     ERROR ("db query utils: The `%s' config option "
999         "needs exactly one string argument.", ci->key);
1000     return (-1);
1001   }
1002   name = ci->values[0].value.string;
1003
1004   return (udb_query_pick_from_list_by_name (name,
1005         src_list, src_list_len,
1006         dst_list, dst_list_len));
1007 } /* }}} int udb_query_pick_from_list */
1008
1009 const char *udb_query_get_name (udb_query_t *q) /* {{{ */
1010 {
1011   if (q == NULL)
1012     return (NULL);
1013
1014   return (q->name);
1015 } /* }}} const char *udb_query_get_name */
1016
1017 const char *udb_query_get_statement (udb_query_t *q) /* {{{ */
1018 {
1019   if (q == NULL)
1020     return (NULL);
1021
1022   return (q->statement);
1023 } /* }}} const char *udb_query_get_statement */
1024
1025 void udb_query_set_user_data (udb_query_t *q, void *user_data) /* {{{ */
1026 {
1027   if (q == NULL)
1028     return;
1029
1030   q->user_data = user_data;
1031 } /* }}} void udb_query_set_user_data */
1032
1033 void *udb_query_get_user_data (udb_query_t *q) /* {{{ */
1034 {
1035   if (q == NULL)
1036     return (NULL);
1037
1038   return (q->user_data);
1039 } /* }}} void *udb_query_get_user_data */
1040
1041 int udb_query_check_version (udb_query_t *q, unsigned int version) /* {{{ */
1042 {
1043   if (q == NULL)
1044     return (-EINVAL);
1045
1046   if ((version < q->min_version) || (version > q->max_version))
1047     return (0);
1048
1049   return (1);
1050 } /* }}} int udb_query_check_version */
1051
1052 void udb_query_finish_result (udb_query_t *q) /* {{{ */
1053 {
1054   udb_result_t *r;
1055
1056   if (q == NULL)
1057     return;
1058
1059   q->column_num = 0;
1060   sfree (q->host);
1061   sfree (q->plugin);
1062   sfree (q->db_name);
1063
1064   for (r = q->results; r != NULL; r = r->next)
1065     udb_result_finish_result (r);
1066 } /* }}} void udb_query_finish_result */
1067
1068 int udb_query_handle_result (udb_query_t *q, char **column_values) /* {{{ */
1069 {
1070   udb_result_t *r;
1071   int success;
1072   int status;
1073
1074   if (q == NULL)
1075     return (-EINVAL);
1076
1077   if ((q->column_num < 1) || (q->host == NULL) || (q->plugin == NULL)
1078       || (q->db_name == NULL))
1079   {
1080     ERROR ("db query utils: Query `%s': Query is not prepared; "
1081         "can't handle result.", q->name);
1082     return (-EINVAL);
1083   }
1084
1085 #if defined(COLLECT_DEBUG) && COLLECT_DEBUG /* {{{ */
1086   do
1087   {
1088     size_t i;
1089
1090     for (i = 0; i < q->column_num; i++)
1091     {
1092       DEBUG ("db query utils: udb_query_handle_result (%s, %s): "
1093           "column[%zu] = %s;",
1094           q->db_name, q->name, i, column_values[i]);
1095     }
1096   } while (0);
1097 #endif /* }}} */
1098
1099   success = 0;
1100   for (r = q->results; r != NULL; r = r->next)
1101   {
1102     status = udb_result_handle_result (r, q, column_values);
1103     if (status == 0)
1104       success++;
1105   }
1106
1107   if (success == 0)
1108   {
1109     ERROR ("db query utils: udb_query_handle_result (%s, %s): "
1110         "All results failed.", q->db_name, q->name);
1111     return (-1);
1112   }
1113
1114   return (0);
1115 } /* }}} int udb_query_handle_result */
1116
1117 int udb_query_prepare_result (udb_query_t *q, /* {{{ */
1118     const char *host, const char *plugin, const char *db_name,
1119     char **column_names, size_t column_num)
1120 {
1121   udb_result_t *r;
1122   int status;
1123
1124   if (q == NULL)
1125     return (-EINVAL);
1126
1127   udb_query_finish_result (q);
1128
1129   q->column_num = column_num;
1130   q->host = strdup (host);
1131   q->plugin = strdup (plugin);
1132   q->db_name = strdup (db_name);
1133
1134   if ((q->host == NULL) || (q->plugin == NULL) || (q->db_name == NULL))
1135   {
1136     ERROR ("db query utils: Query `%s': Prepare failed: Out of memory.", q->name);
1137     udb_query_finish_result (q);
1138     return (-ENOMEM);
1139   }
1140
1141 #if defined(COLLECT_DEBUG) && COLLECT_DEBUG
1142   do
1143   {
1144     size_t i;
1145
1146     for (i = 0; i < column_num; i++)
1147     {
1148       DEBUG ("db query utils: udb_query_prepare_result: "
1149           "query = %s; column[%zu] = %s;",
1150           q->name, i, column_names[i]);
1151     }
1152   } while (0);
1153 #endif
1154
1155   for (r = q->results; r != NULL; r = r->next)
1156   {
1157     status = udb_result_prepare_result (r, column_names, column_num);
1158     if (status != 0)
1159     {
1160       udb_query_finish_result (q);
1161       return (status);
1162     }
1163   }
1164
1165   return (0);
1166 } /* }}} int udb_query_prepare_result */
1167
1168 /* vim: set sw=2 sts=2 et fdm=marker : */