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