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