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