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