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