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