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