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 **/
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
26 #include "utils_db_query.h"
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;
42 /* Legacy data */
43 int legacy_mode;
44 size_t legacy_position;
45 /* When in legacy mode:
46 * - type/ds hold the format of the data
47 * - instance_prefix is used as type-instance if non-NULL
48 * - legacy_position holds the index of the column to use as value.
49 */
51 udb_result_t *next;
52 }; /* }}} */
54 struct udb_query_s /* {{{ */
55 {
56 char *name;
57 char *statement;
58 void *user_data;
60 int legacy_mode;
62 unsigned int min_version;
63 unsigned int max_version;
65 udb_result_t *results;
66 }; /* }}} */
68 struct udb_result_preparation_area_s /* {{{ */
69 {
70 const data_set_t *ds;
71 size_t *instances_pos;
72 size_t *values_pos;
73 char **instances_buffer;
74 char **values_buffer;
76 struct udb_result_preparation_area_s *next;
77 }; /* }}} */
78 typedef struct udb_result_preparation_area_s udb_result_preparation_area_t;
80 struct udb_query_preparation_area_s /* {{{ */
81 {
82 size_t column_num;
83 char *host;
84 char *plugin;
85 char *db_name;
87 udb_result_preparation_area_t *result_prep_areas;
88 }; /* }}} */
90 /*
91 * Config Private functions
92 */
93 static int udb_config_set_string (char **ret_string, /* {{{ */
94 oconfig_item_t *ci)
95 {
96 char *string;
98 if ((ci->values_num != 1)
99 || (ci->values[0].type != OCONFIG_TYPE_STRING))
100 {
101 WARNING ("db query utils: The `%s' config option "
102 "needs exactly one string argument.", ci->key);
103 return (-1);
104 }
106 string = strdup (ci->values[0].value.string);
107 if (string == NULL)
108 {
109 ERROR ("db query utils: strdup failed.");
110 return (-1);
111 }
113 if (*ret_string != NULL)
114 free (*ret_string);
115 *ret_string = string;
117 return (0);
118 } /* }}} int udb_config_set_string */
120 static int udb_config_add_string (char ***ret_array, /* {{{ */
121 size_t *ret_array_len, oconfig_item_t *ci)
122 {
123 char **array;
124 size_t array_len;
125 int i;
127 if (ci->values_num < 1)
128 {
129 WARNING ("db query utils: The `%s' config option "
130 "needs at least one argument.", ci->key);
131 return (-1);
132 }
134 for (i = 0; i < ci->values_num; i++)
135 {
136 if (ci->values[i].type != OCONFIG_TYPE_STRING)
137 {
138 WARNING ("db query utils: Argument %i to the `%s' option "
139 "is not a string.", i + 1, ci->key);
140 return (-1);
141 }
142 }
144 array_len = *ret_array_len;
145 array = (char **) realloc (*ret_array,
146 sizeof (char *) * (array_len + ci->values_num));
147 if (array == NULL)
148 {
149 ERROR ("db query utils: realloc failed.");
150 return (-1);
151 }
152 *ret_array = array;
154 for (i = 0; i < ci->values_num; i++)
155 {
156 array[array_len] = strdup (ci->values[i].value.string);
157 if (array[array_len] == NULL)
158 {
159 ERROR ("db query utils: strdup failed.");
160 *ret_array_len = array_len;
161 return (-1);
162 }
163 array_len++;
164 }
166 *ret_array_len = array_len;
167 return (0);
168 } /* }}} int udb_config_add_string */
170 static int udb_config_set_uint (unsigned int *ret_value, /* {{{ */
171 oconfig_item_t *ci)
172 {
173 double tmp;
175 if ((ci->values_num != 1)
176 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
177 {
178 WARNING ("db query utils: The `%s' config option "
179 "needs exactly one numeric argument.", ci->key);
180 return (-1);
181 }
183 tmp = ci->values[0].value.number;
184 if ((tmp < 0.0) || (tmp > ((double) UINT_MAX)))
185 return (-ERANGE);
187 *ret_value = (unsigned int) (tmp + .5);
188 return (0);
189 } /* }}} int udb_config_set_uint */
191 /*
192 * Legacy result private functions
193 */
194 static void udb_legacy_result_finish_result (const udb_result_t const *r, /* {{{ */
195 udb_result_preparation_area_t *prep_area)
196 {
197 if ((r == NULL) || (prep_area))
198 return;
200 assert (r->legacy_mode == 1);
202 prep_area->ds = NULL;
203 } /* }}} void udb_legacy_result_finish_result */
205 static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
206 udb_query_preparation_area_t *q_area,
207 udb_result_preparation_area_t *r_area,
208 const udb_query_t const *q, char **column_values)
209 {
210 value_list_t vl = VALUE_LIST_INIT;
211 value_t value;
212 char *value_str;
214 assert (r->legacy_mode == 1);
215 assert (r_area->ds != NULL);
216 assert (r_area->ds->ds_num == 1);
218 vl.values = &value;
219 vl.values_len = 1;
221 value_str = column_values[r->legacy_position];
222 if (0 != parse_value (value_str, &vl.values[0], r_area->ds->ds[0].type))
223 {
224 ERROR ("db query utils: udb_legacy_result_handle_result: "
225 "Parsing `%s' as %s failed.", value_str,
226 DS_TYPE_TO_STRING (r_area->ds->ds[0].type));
227 errno = EINVAL;
228 return (-1);
229 }
231 sstrncpy (vl.host, q_area->host, sizeof (vl.host));
232 sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
233 sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
234 sstrncpy (vl.type, r->type, sizeof (vl.type));
236 if (r->instance_prefix != NULL)
237 sstrncpy (vl.type_instance, r->instance_prefix,
238 sizeof (vl.type_instance));
240 plugin_dispatch_values (&vl);
242 return (0);
243 } /* }}} int udb_legacy_result_handle_result */
245 static int udb_legacy_result_prepare_result (const udb_result_t const *r, /* {{{ */
246 udb_result_preparation_area_t *prep_area,
247 char **column_names, size_t column_num)
248 {
249 if (r == NULL)
250 return (-EINVAL);
252 assert (r->legacy_mode == 1);
254 /* Make sure previous preparations are cleaned up. */
255 udb_legacy_result_finish_result (r, prep_area);
257 if (r->legacy_position >= column_num)
258 {
259 ERROR ("db query utils: The legacy configuration specified (at least) "
260 "%zu `Column's, but the query returned only %zu columns!",
261 r->legacy_position + 1, column_num);
262 return (-ENOENT);
263 }
265 /* Read `ds' and check number of values {{{ */
266 prep_area->ds = plugin_get_ds (r->type);
267 if (prep_area->ds == NULL)
268 {
269 ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
270 "known by the daemon. See types.db(5) for details.",
271 r->type);
272 return (-1);
273 }
275 if (prep_area->ds->ds_num != 1)
276 {
277 ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
278 "requires exactly %i values, but the legacy configuration "
279 "requires exactly one!",
280 r->type,
281 prep_area->ds->ds_num);
282 return (-1);
283 }
284 /* }}} */
286 return (0);
287 } /* }}} int udb_legacy_result_prepare_result */
289 static int udb_legacy_result_create (const char *query_name, /* {{{ */
290 udb_result_t **r_head, oconfig_item_t *ci, size_t position)
291 {
292 udb_result_t *r;
294 if ((ci->values_num < 1) || (ci->values_num > 2)
295 || (ci->values[0].type != OCONFIG_TYPE_STRING)
296 || ((ci->values_num == 2)
297 && (ci->values[1].type != OCONFIG_TYPE_STRING)))
298 {
299 WARNING ("db query utils: The `Column' block needs either one or two "
300 "string arguments.");
301 return (-1);
302 }
304 r = (udb_result_t *) malloc (sizeof (*r));
305 if (r == NULL)
306 {
307 ERROR ("db query utils: malloc failed.");
308 return (-1);
309 }
310 memset (r, 0, sizeof (*r));
312 r->legacy_mode = 1;
313 r->legacy_position = position;
315 r->type = strdup (ci->values[0].value.string);
316 if (r->type == NULL)
317 {
318 ERROR ("db query utils: strdup failed.");
319 free (r);
320 return (-1);
321 }
323 r->instance_prefix = NULL;
324 if (ci->values_num == 2)
325 {
326 r->instance_prefix = strdup (ci->values[1].value.string);
327 if (r->instance_prefix == NULL)
328 {
329 ERROR ("db query utils: strdup failed.");
330 free (r->type);
331 free (r);
332 return (-1);
333 }
334 }
336 /* If all went well, add this result to the list of results. */
337 if (*r_head == NULL)
338 {
339 *r_head = r;
340 }
341 else
342 {
343 udb_result_t *last;
345 last = *r_head;
346 while (last->next != NULL)
347 last = last->next;
349 last->next = r;
350 }
352 return (0);
353 } /* }}} int udb_legacy_result_create */
355 /*
356 * Result private functions
357 */
358 static int udb_result_submit (udb_result_t *r, /* {{{ */
359 udb_result_preparation_area_t *r_area,
360 const udb_query_t const *q, udb_query_preparation_area_t *q_area)
361 {
362 value_list_t vl = VALUE_LIST_INIT;
363 size_t i;
365 assert (r != NULL);
366 assert (r->legacy_mode == 0);
367 assert (r_area->ds != NULL);
368 assert (((size_t) r_area->ds->ds_num) == r->values_num);
370 vl.values = (value_t *) calloc (r_area->ds->ds_num, sizeof (value_t));
371 if (vl.values == NULL)
372 {
373 ERROR ("db query utils: malloc failed.");
374 return (-1);
375 }
376 vl.values_len = r_area->ds->ds_num;
378 for (i = 0; i < r->values_num; i++)
379 {
380 char *value_str = r_area->values_buffer[i];
382 if (0 != parse_value (value_str, &vl.values[i], r_area->ds->ds[i].type))
383 {
384 ERROR ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
385 value_str, DS_TYPE_TO_STRING (r_area->ds->ds[i].type));
386 errno = EINVAL;
387 return (-1);
388 }
389 }
391 sstrncpy (vl.host, q_area->host, sizeof (vl.host));
392 sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
393 sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
394 sstrncpy (vl.type, r->type, sizeof (vl.type));
396 /* Set vl.type_instance {{{ */
397 if (r->instances_num <= 0)
398 {
399 if (r->instance_prefix == NULL)
400 vl.type_instance[0] = 0;
401 else
402 sstrncpy (vl.type_instance, r->instance_prefix,
403 sizeof (vl.type_instance));
404 }
405 else /* if ((r->instances_num > 0) */
406 {
407 if (r->instance_prefix == NULL)
408 {
409 strjoin (vl.type_instance, sizeof (vl.type_instance),
410 r_area->instances_buffer, r->instances_num, "-");
411 }
412 else
413 {
414 char tmp[DATA_MAX_NAME_LEN];
416 strjoin (tmp, sizeof (tmp), r_area->instances_buffer,
417 r->instances_num, "-");
418 tmp[sizeof (tmp) - 1] = 0;
420 snprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
421 r->instance_prefix, tmp);
422 }
423 }
424 vl.type_instance[sizeof (vl.type_instance) - 1] = 0;
425 /* }}} */
427 plugin_dispatch_values (&vl);
429 sfree (vl.values);
430 return (0);
431 } /* }}} void udb_result_submit */
433 static void udb_result_finish_result (const udb_result_t const *r, /* {{{ */
434 udb_result_preparation_area_t *prep_area)
435 {
436 if ((r == NULL) || (prep_area == NULL))
437 return;
439 if (r->legacy_mode == 1)
440 {
441 udb_legacy_result_finish_result (r, prep_area);
442 return;
443 }
445 assert (r->legacy_mode == 0);
447 prep_area->ds = NULL;
448 sfree (prep_area->instances_pos);
449 sfree (prep_area->values_pos);
450 sfree (prep_area->instances_buffer);
451 sfree (prep_area->values_buffer);
452 } /* }}} void udb_result_finish_result */
454 static int udb_result_handle_result (udb_result_t *r, /* {{{ */
455 udb_query_preparation_area_t *q_area,
456 udb_result_preparation_area_t *r_area,
457 const udb_query_t const *q, char **column_values)
458 {
459 size_t i;
461 assert (r && q_area && r_area);
463 if (r->legacy_mode == 1)
464 return (udb_legacy_result_handle_result (r, q_area, r_area,
465 q, column_values));
467 assert (r->legacy_mode == 0);
469 for (i = 0; i < r->instances_num; i++)
470 r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]];
472 for (i = 0; i < r->values_num; i++)
473 r_area->values_buffer[i] = column_values[r_area->values_pos[i]];
475 return udb_result_submit (r, r_area, q, q_area);
476 } /* }}} int udb_result_handle_result */
478 static int udb_result_prepare_result (const udb_result_t const *r, /* {{{ */
479 udb_result_preparation_area_t *prep_area,
480 char **column_names, size_t column_num)
481 {
482 size_t i;
484 if ((r == NULL) || (prep_area == NULL))
485 return (-EINVAL);
487 if (r->legacy_mode == 1)
488 return (udb_legacy_result_prepare_result (r, prep_area,
489 column_names, column_num));
491 assert (r->legacy_mode == 0);
493 #define BAIL_OUT(status) \
494 prep_area->ds = NULL; \
495 sfree (prep_area->instances_pos); \
496 sfree (prep_area->values_pos); \
497 sfree (prep_area->instances_buffer); \
498 sfree (prep_area->values_buffer); \
499 return (status)
501 /* Make sure previous preparations are cleaned up. */
502 udb_result_finish_result (r, prep_area);
503 prep_area->instances_pos = NULL;
504 prep_area->values_pos = NULL;
506 /* Read `ds' and check number of values {{{ */
507 prep_area->ds = plugin_get_ds (r->type);
508 if (prep_area->ds == NULL)
509 {
510 ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
511 "known by the daemon. See types.db(5) for details.",
512 r->type);
513 BAIL_OUT (-1);
514 }
516 if (((size_t) prep_area->ds->ds_num) != r->values_num)
517 {
518 ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
519 "requires exactly %i value%s, but the configuration specifies %zu.",
520 r->type,
521 prep_area->ds->ds_num, (prep_area->ds->ds_num == 1) ? "" : "s",
522 r->values_num);
523 BAIL_OUT (-1);
524 }
525 /* }}} */
527 /* Allocate r->instances_pos, r->values_pos, r->instances_buffer, and
528 * r->values_buffer {{{ */
529 if (r->instances_num > 0)
530 {
531 prep_area->instances_pos
532 = (size_t *) calloc (r->instances_num, sizeof (size_t));
533 if (prep_area->instances_pos == NULL)
534 {
535 ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
536 BAIL_OUT (-ENOMEM);
537 }
539 prep_area->instances_buffer
540 = (char **) calloc (r->instances_num, sizeof (char *));
541 if (prep_area->instances_buffer == NULL)
542 {
543 ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
544 BAIL_OUT (-ENOMEM);
545 }
546 } /* if (r->instances_num > 0) */
548 prep_area->values_pos
549 = (size_t *) calloc (r->values_num, sizeof (size_t));
550 if (prep_area->values_pos == NULL)
551 {
552 ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
553 BAIL_OUT (-ENOMEM);
554 }
556 prep_area->values_buffer
557 = (char **) calloc (r->values_num, sizeof (char *));
558 if (prep_area->values_buffer == NULL)
559 {
560 ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
561 BAIL_OUT (-ENOMEM);
562 }
563 /* }}} */
565 /* Determine the position of the instance columns {{{ */
566 for (i = 0; i < r->instances_num; i++)
567 {
568 size_t j;
570 for (j = 0; j < column_num; j++)
571 {
572 if (strcasecmp (r->instances[i], column_names[j]) == 0)
573 {
574 prep_area->instances_pos[i] = j;
575 break;
576 }
577 }
579 if (j >= column_num)
580 {
581 ERROR ("db query utils: udb_result_prepare_result: "
582 "Column `%s' could not be found.",
583 r->instances[i]);
584 BAIL_OUT (-ENOENT);
585 }
586 } /* }}} for (i = 0; i < r->instances_num; i++) */
588 /* Determine the position of the value columns {{{ */
589 for (i = 0; i < r->values_num; i++)
590 {
591 size_t j;
593 for (j = 0; j < column_num; j++)
594 {
595 if (strcasecmp (r->values[i], column_names[j]) == 0)
596 {
597 prep_area->values_pos[i] = j;
598 break;
599 }
600 }
602 if (j >= column_num)
603 {
604 ERROR ("db query utils: udb_result_prepare_result: "
605 "Column `%s' could not be found.",
606 r->values[i]);
607 BAIL_OUT (-ENOENT);
608 }
609 } /* }}} for (i = 0; i < r->values_num; i++) */
611 #undef BAIL_OUT
612 return (0);
613 } /* }}} int udb_result_prepare_result */
615 static void udb_result_free (udb_result_t *r) /* {{{ */
616 {
617 size_t i;
619 if (r == NULL)
620 return;
622 sfree (r->type);
624 for (i = 0; i < r->instances_num; i++)
625 sfree (r->instances[i]);
626 sfree (r->instances);
628 for (i = 0; i < r->values_num; i++)
629 sfree (r->values[i]);
630 sfree (r->values);
632 udb_result_free (r->next);
634 sfree (r);
635 } /* }}} void udb_result_free */
637 static int udb_result_create (const char *query_name, /* {{{ */
638 udb_result_t **r_head, oconfig_item_t *ci)
639 {
640 udb_result_t *r;
641 int status;
642 int i;
644 if (ci->values_num != 0)
645 {
646 WARNING ("db query utils: The `Result' block doesn't accept "
647 "any arguments. Ignoring %i argument%s.",
648 ci->values_num, (ci->values_num == 1) ? "" : "s");
649 }
651 r = (udb_result_t *) malloc (sizeof (*r));
652 if (r == NULL)
653 {
654 ERROR ("db query utils: malloc failed.");
655 return (-1);
656 }
657 memset (r, 0, sizeof (*r));
658 r->type = NULL;
659 r->instance_prefix = NULL;
660 r->instances = NULL;
661 r->values = NULL;
662 r->next = NULL;
664 /* Fill the `udb_result_t' structure.. */
665 status = 0;
666 for (i = 0; i < ci->children_num; i++)
667 {
668 oconfig_item_t *child = ci->children + i;
670 if (strcasecmp ("Type", child->key) == 0)
671 status = udb_config_set_string (&r->type, child);
672 else if (strcasecmp ("InstancePrefix", child->key) == 0)
673 status = udb_config_set_string (&r->instance_prefix, child);
674 else if (strcasecmp ("InstancesFrom", child->key) == 0)
675 status = udb_config_add_string (&r->instances, &r->instances_num, child);
676 else if (strcasecmp ("ValuesFrom", child->key) == 0)
677 status = udb_config_add_string (&r->values, &r->values_num, child);
678 else
679 {
680 WARNING ("db query utils: Query `%s': Option `%s' not allowed here.",
681 query_name, child->key);
682 status = -1;
683 }
685 if (status != 0)
686 break;
687 }
689 /* Check that all necessary options have been given. */
690 while (status == 0)
691 {
692 if (r->type == NULL)
693 {
694 WARNING ("db query utils: `Type' not given for "
695 "result in query `%s'", query_name);
696 status = -1;
697 }
698 if (r->values == NULL)
699 {
700 WARNING ("db query utils: `ValuesFrom' not given for "
701 "result in query `%s'", query_name);
702 status = -1;
703 }
705 break;
706 } /* while (status == 0) */
708 if (status != 0)
709 {
710 udb_result_free (r);
711 return (-1);
712 }
714 /* If all went well, add this result to the list of results. */
715 if (*r_head == NULL)
716 {
717 *r_head = r;
718 }
719 else
720 {
721 udb_result_t *last;
723 last = *r_head;
724 while (last->next != NULL)
725 last = last->next;
727 last->next = r;
728 }
730 return (0);
731 } /* }}} int udb_result_create */
733 /*
734 * Query private functions
735 */
736 void udb_query_free_one (udb_query_t *q) /* {{{ */
737 {
738 if (q == NULL)
739 return;
741 sfree (q->name);
742 sfree (q->statement);
744 udb_result_free (q->results);
746 sfree (q);
747 } /* }}} void udb_query_free_one */
749 /*
750 * Query public functions
751 */
752 int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */
753 size_t *ret_query_list_len, oconfig_item_t *ci,
754 udb_query_create_callback_t cb, int legacy_mode)
755 {
756 udb_query_t **query_list;
757 size_t query_list_len;
759 udb_query_t *q;
760 int status;
761 int i;
763 size_t legacy_position;
765 if ((ret_query_list == NULL) || (ret_query_list_len == NULL))
766 return (-EINVAL);
767 query_list = *ret_query_list;
768 query_list_len = *ret_query_list_len;
770 if ((ci->values_num != 1)
771 || (ci->values[0].type != OCONFIG_TYPE_STRING))
772 {
773 WARNING ("db query utils: The `Query' block "
774 "needs exactly one string argument.");
775 return (-1);
776 }
778 q = (udb_query_t *) malloc (sizeof (*q));
779 if (q == NULL)
780 {
781 ERROR ("db query utils: malloc failed.");
782 return (-1);
783 }
784 memset (q, 0, sizeof (*q));
785 q->legacy_mode = legacy_mode;
786 q->min_version = 0;
787 q->max_version = UINT_MAX;
789 legacy_position = 0;
791 status = udb_config_set_string (&q->name, ci);
792 if (status != 0)
793 {
794 sfree (q);
795 return (status);
796 }
798 /* Fill the `udb_query_t' structure.. */
799 for (i = 0; i < ci->children_num; i++)
800 {
801 oconfig_item_t *child = ci->children + i;
803 if (strcasecmp ("Statement", child->key) == 0)
804 status = udb_config_set_string (&q->statement, child);
805 else if (strcasecmp ("Result", child->key) == 0)
806 status = udb_result_create (q->name, &q->results, child);
807 else if (strcasecmp ("MinVersion", child->key) == 0)
808 status = udb_config_set_uint (&q->min_version, child);
809 else if (strcasecmp ("MaxVersion", child->key) == 0)
810 status = udb_config_set_uint (&q->max_version, child);
812 /* PostgreSQL compatibility code */
813 else if ((strcasecmp ("Query", child->key) == 0)
814 && (q->legacy_mode == 1))
815 {
816 WARNING ("db query utils: Query `%s': The `Query' option is "
817 "deprecated. Please use `Statement' instead.",
818 q->name);
819 status = udb_config_set_string (&q->statement, child);
820 }
821 else if ((strcasecmp ("Column", child->key) == 0)
822 && (q->legacy_mode == 1))
823 {
824 WARNING ("db query utils: Query `%s': The `Column' option is "
825 "deprecated. Please use the new syntax instead.",
826 q->name);
827 status = udb_legacy_result_create (q->name, &q->results, child,
828 legacy_position);
829 legacy_position++;
830 }
831 else if ((strcasecmp ("MinPGVersion", child->key) == 0)
832 && (q->legacy_mode == 1))
833 {
834 WARNING ("db query utils: Query `%s': The `MinPGVersion' option is "
835 "deprecated. Please use `MinVersion' instead.",
836 q->name);
837 status = udb_config_set_uint (&q->min_version, child);
838 }
839 else if ((strcasecmp ("MaxPGVersion", child->key) == 0)
840 && (q->legacy_mode == 1))
841 {
842 WARNING ("db query utils: Query `%s': The `MaxPGVersion' option is "
843 "deprecated. Please use `MaxVersion' instead.",
844 q->name);
845 status = udb_config_set_uint (&q->max_version, child);
846 }
848 /* Call custom callbacks */
849 else if (cb != NULL)
850 {
851 status = (*cb) (q, child);
852 if (status != 0)
853 {
854 WARNING ("db query utils: The configuration callback failed "
855 "to handle `%s'.", child->key);
856 }
857 }
858 else
859 {
860 WARNING ("db query utils: Query `%s': Option `%s' not allowed here.",
861 q->name, child->key);
862 status = -1;
863 }
865 if (status != 0)
866 break;
867 }
869 /* Check that all necessary options have been given. */
870 if (status == 0)
871 {
872 if (q->statement == NULL)
873 {
874 WARNING ("db query utils: Query `%s': No `Statement' given.", q->name);
875 status = -1;
876 }
877 if (q->results == NULL)
878 {
879 WARNING ("db query utils: Query `%s': No (valid) `Result' block given.",
880 q->name);
881 status = -1;
882 }
883 } /* if (status == 0) */
885 /* If all went well, add this query to the list of queries within the
886 * database structure. */
887 if (status == 0)
888 {
889 udb_query_t **temp;
891 temp = (udb_query_t **) realloc (query_list,
892 sizeof (*query_list) * (query_list_len + 1));
893 if (temp == NULL)
894 {
895 ERROR ("db query utils: realloc failed");
896 status = -1;
897 }
898 else
899 {
900 query_list = temp;
901 query_list[query_list_len] = q;
902 query_list_len++;
903 }
904 }
906 if (status != 0)
907 {
908 udb_query_free_one (q);
909 return (-1);
910 }
912 *ret_query_list = query_list;
913 *ret_query_list_len = query_list_len;
915 return (0);
916 } /* }}} int udb_query_create */
918 void udb_query_free (udb_query_t **query_list, size_t query_list_len) /* {{{ */
919 {
920 size_t i;
922 if (query_list == NULL)
923 return;
925 for (i = 0; i < query_list_len; i++)
926 udb_query_free_one (query_list[i]);
928 sfree (query_list);
929 } /* }}} void udb_query_free */
931 int udb_query_pick_from_list_by_name (const char *name, /* {{{ */
932 udb_query_t **src_list, size_t src_list_len,
933 udb_query_t ***dst_list, size_t *dst_list_len)
934 {
935 size_t i;
936 int num_added;
938 if ((name == NULL) || (src_list == NULL) || (dst_list == NULL)
939 || (dst_list_len == NULL))
940 {
941 ERROR ("db query utils: udb_query_pick_from_list_by_name: "
942 "Invalid argument.");
943 return (-EINVAL);
944 }
946 num_added = 0;
947 for (i = 0; i < src_list_len; i++)
948 {
949 udb_query_t **tmp_list;
950 size_t tmp_list_len;
952 if (strcasecmp (name, src_list[i]->name) != 0)
953 continue;
955 tmp_list_len = *dst_list_len;
956 tmp_list = (udb_query_t **) realloc (*dst_list, (tmp_list_len + 1)
957 * sizeof (udb_query_t *));
958 if (tmp_list == NULL)
959 {
960 ERROR ("db query utils: realloc failed.");
961 return (-ENOMEM);
962 }
964 tmp_list[tmp_list_len] = src_list[i];
965 tmp_list_len++;
967 *dst_list = tmp_list;
968 *dst_list_len = tmp_list_len;
970 num_added++;
971 } /* for (i = 0; i < src_list_len; i++) */
973 if (num_added <= 0)
974 {
975 ERROR ("db query utils: Cannot find query `%s'. Make sure the <Query> "
976 "block is above the database definition!",
977 name);
978 return (-ENOENT);
979 }
980 else
981 {
982 DEBUG ("db query utils: Added %i versions of query `%s'.",
983 num_added, name);
984 }
986 return (0);
987 } /* }}} int udb_query_pick_from_list_by_name */
989 int udb_query_pick_from_list (oconfig_item_t *ci, /* {{{ */
990 udb_query_t **src_list, size_t src_list_len,
991 udb_query_t ***dst_list, size_t *dst_list_len)
992 {
993 const char *name;
995 if ((ci == NULL) || (src_list == NULL) || (dst_list == NULL)
996 || (dst_list_len == NULL))
997 {
998 ERROR ("db query utils: udb_query_pick_from_list: "
999 "Invalid argument.");
1000 return (-EINVAL);
1001 }
1003 if ((ci->values_num != 1)
1004 || (ci->values[0].type != OCONFIG_TYPE_STRING))
1005 {
1006 ERROR ("db query utils: The `%s' config option "
1007 "needs exactly one string argument.", ci->key);
1008 return (-1);
1009 }
1010 name = ci->values[0].value.string;
1012 return (udb_query_pick_from_list_by_name (name,
1013 src_list, src_list_len,
1014 dst_list, dst_list_len));
1015 } /* }}} int udb_query_pick_from_list */
1017 const char *udb_query_get_name (udb_query_t *q) /* {{{ */
1018 {
1019 if (q == NULL)
1020 return (NULL);
1022 return (q->name);
1023 } /* }}} const char *udb_query_get_name */
1025 const char *udb_query_get_statement (udb_query_t *q) /* {{{ */
1026 {
1027 if (q == NULL)
1028 return (NULL);
1030 return (q->statement);
1031 } /* }}} const char *udb_query_get_statement */
1033 void udb_query_set_user_data (udb_query_t *q, void *user_data) /* {{{ */
1034 {
1035 if (q == NULL)
1036 return;
1038 q->user_data = user_data;
1039 } /* }}} void udb_query_set_user_data */
1041 void *udb_query_get_user_data (udb_query_t *q) /* {{{ */
1042 {
1043 if (q == NULL)
1044 return (NULL);
1046 return (q->user_data);
1047 } /* }}} void *udb_query_get_user_data */
1049 int udb_query_check_version (udb_query_t *q, unsigned int version) /* {{{ */
1050 {
1051 if (q == NULL)
1052 return (-EINVAL);
1054 if ((version < q->min_version) || (version > q->max_version))
1055 return (0);
1057 return (1);
1058 } /* }}} int udb_query_check_version */
1060 void udb_query_finish_result (const udb_query_t const *q, /* {{{ */
1061 udb_query_preparation_area_t *prep_area)
1062 {
1063 udb_result_preparation_area_t *r_area;
1064 udb_result_t *r;
1066 if ((q == NULL) || (prep_area == NULL))
1067 return;
1069 prep_area->column_num = 0;
1070 sfree (prep_area->host);
1071 sfree (prep_area->plugin);
1072 sfree (prep_area->db_name);
1074 for (r = q->results, r_area = prep_area->result_prep_areas;
1075 r != NULL; r = r->next, r_area = r_area->next)
1076 {
1077 /* this may happen during error conditions of the caller */
1078 if (r_area == NULL)
1079 break;
1080 udb_result_finish_result (r, r_area);
1081 }
1082 } /* }}} void udb_query_finish_result */
1084 int udb_query_handle_result (const udb_query_t const *q, /* {{{ */
1085 udb_query_preparation_area_t *prep_area, char **column_values)
1086 {
1087 udb_result_preparation_area_t *r_area;
1088 udb_result_t *r;
1089 int success;
1090 int status;
1092 if ((q == NULL) || (prep_area == NULL))
1093 return (-EINVAL);
1095 if ((prep_area->column_num < 1) || (prep_area->host == NULL)
1096 || (prep_area->plugin == NULL) || (prep_area->db_name == NULL))
1097 {
1098 ERROR ("db query utils: Query `%s': Query is not prepared; "
1099 "can't handle result.", q->name);
1100 return (-EINVAL);
1101 }
1103 #if defined(COLLECT_DEBUG) && COLLECT_DEBUG /* {{{ */
1104 do
1105 {
1106 size_t i;
1108 for (i = 0; i < prep_area->column_num; i++)
1109 {
1110 DEBUG ("db query utils: udb_query_handle_result (%s, %s): "
1111 "column[%zu] = %s;",
1112 prep_area->db_name, q->name, i, column_values[i]);
1113 }
1114 } while (0);
1115 #endif /* }}} */
1117 success = 0;
1118 for (r = q->results, r_area = prep_area->result_prep_areas;
1119 r != NULL; r = r->next, r_area = r_area->next)
1120 {
1121 status = udb_result_handle_result (r, prep_area, r_area,
1122 q, column_values);
1123 if (status == 0)
1124 success++;
1125 }
1127 if (success == 0)
1128 {
1129 ERROR ("db query utils: udb_query_handle_result (%s, %s): "
1130 "All results failed.", prep_area->db_name, q->name);
1131 return (-1);
1132 }
1134 return (0);
1135 } /* }}} int udb_query_handle_result */
1137 int udb_query_prepare_result (const udb_query_t const *q, /* {{{ */
1138 udb_query_preparation_area_t *prep_area,
1139 const char *host, const char *plugin, const char *db_name,
1140 char **column_names, size_t column_num)
1141 {
1142 udb_result_preparation_area_t *r_area;
1143 udb_result_t *r;
1144 int status;
1146 if ((q == NULL) || (prep_area == NULL))
1147 return (-EINVAL);
1149 udb_query_finish_result (q, prep_area);
1151 prep_area->column_num = column_num;
1152 prep_area->host = strdup (host);
1153 prep_area->plugin = strdup (plugin);
1154 prep_area->db_name = strdup (db_name);
1156 if ((prep_area->host == NULL) || (prep_area->plugin == NULL)
1157 || (prep_area->db_name == NULL))
1158 {
1159 ERROR ("db query utils: Query `%s': Prepare failed: Out of memory.", q->name);
1160 udb_query_finish_result (q, prep_area);
1161 return (-ENOMEM);
1162 }
1164 #if defined(COLLECT_DEBUG) && COLLECT_DEBUG
1165 do
1166 {
1167 size_t i;
1169 for (i = 0; i < column_num; i++)
1170 {
1171 DEBUG ("db query utils: udb_query_prepare_result: "
1172 "query = %s; column[%zu] = %s;",
1173 q->name, i, column_names[i]);
1174 }
1175 } while (0);
1176 #endif
1178 for (r = q->results, r_area = prep_area->result_prep_areas;
1179 r != NULL; r = r->next, r_area = r_area->next)
1180 {
1181 if (! r_area)
1182 {
1183 ERROR ("db query utils: Query `%s': Invalid number of result "
1184 "preparation areas.", q->name);
1185 udb_query_finish_result (q, prep_area);
1186 return (-EINVAL);
1187 }
1189 status = udb_result_prepare_result (r, r_area, column_names, column_num);
1190 if (status != 0)
1191 {
1192 udb_query_finish_result (q, prep_area);
1193 return (status);
1194 }
1195 }
1197 return (0);
1198 } /* }}} int udb_query_prepare_result */
1200 udb_query_preparation_area_t *
1201 udb_query_allocate_preparation_area (udb_query_t *q) /* {{{ */
1202 {
1203 udb_query_preparation_area_t *q_area;
1204 udb_result_preparation_area_t **next_r_area;
1205 udb_result_t *r;
1207 q_area = (udb_query_preparation_area_t *)malloc (sizeof (*q_area));
1208 if (q_area == NULL)
1209 return NULL;
1211 memset (q_area, 0, sizeof (*q_area));
1213 next_r_area = &q_area->result_prep_areas;
1214 for (r = q->results; r != NULL; r = r->next)
1215 {
1216 udb_result_preparation_area_t *r_area;
1218 r_area = (udb_result_preparation_area_t *)malloc (sizeof (*r_area));
1219 if (r_area == NULL)
1220 {
1221 for (r_area = q_area->result_prep_areas;
1222 r_area != NULL; r_area = r_area->next)
1223 {
1224 free (r_area);
1225 }
1226 free (q_area);
1227 return NULL;
1228 }
1230 memset (r_area, 0, sizeof (*r_area));
1232 *next_r_area = r_area;
1233 next_r_area = &r_area->next;
1234 }
1236 return (q_area);
1237 } /* }}} udb_query_preparation_area_t *udb_query_allocate_preparation_area */
1239 void
1240 udb_query_delete_preparation_area (udb_query_preparation_area_t *q_area) /* {{{ */
1241 {
1242 udb_result_preparation_area_t *r_area;
1244 if (q_area == NULL)
1245 return;
1247 r_area = q_area->result_prep_areas;
1248 while (r_area != NULL)
1249 {
1250 udb_result_preparation_area_t *area = r_area;
1252 r_area = r_area->next;
1254 sfree (area->instances_pos);
1255 sfree (area->values_pos);
1256 sfree (area->instances_buffer);
1257 sfree (area->values_buffer);
1258 free (area);
1259 }
1261 sfree (q_area->host);
1262 sfree (q_area->plugin);
1263 sfree (q_area->db_name);
1265 free (q_area);
1266 } /* }}} void udb_query_delete_preparation_area */
1268 /* vim: set sw=2 sts=2 et fdm=marker : */