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