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