X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Futils_db_query.c;h=5f892a408b0c49f14e4560ad34100bfadfa0f398;hb=9c98fa31ef50a6ff849d36cac4f5297faa6f7909;hp=eb001dee93c1ef55356e1fa805d9c12dbfe82d8c;hpb=d54dfab9b049de396e6a38d690d5f18917e093ea;p=collectd.git diff --git a/src/utils_db_query.c b/src/utils_db_query.c index eb001dee..5f892a40 100644 --- a/src/utils_db_query.c +++ b/src/utils_db_query.c @@ -46,6 +46,15 @@ struct udb_result_s char **instances_buffer; char **values_buffer; + /* Legacy data */ + int legacy_mode; + size_t legacy_position; + /* When in legacy mode: + * - type/ds hold the format of the data + * - instance_prefix is used as type-instance if non-NULL + * - legacy_position holds the index of the column to use as value. + */ + udb_result_t *next; }; /* }}} */ @@ -55,6 +64,8 @@ struct udb_query_s /* {{{ */ char *statement; void *user_data; + int legacy_mode; + unsigned int min_version; unsigned int max_version; @@ -169,51 +180,196 @@ static int udb_config_set_uint (unsigned int *ret_value, /* {{{ */ } /* }}} int udb_config_set_uint */ /* - * Result private functions + * Legacy result private functions */ -static void udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */ +static void udb_legacy_result_finish_result (udb_result_t *r) /* {{{ */ +{ + if (r == NULL) + return; + + assert (r->legacy_mode == 1); + + r->ds = NULL; +} /* }}} void udb_legacy_result_finish_result */ + +static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */ + udb_query_t *q, char **column_values) { value_list_t vl = VALUE_LIST_INIT; - size_t i; + value_t value; + char *value_str; - assert (((size_t) r->ds->ds_num) == r->values_num); + assert (r->legacy_mode == 1); + assert (r->ds != NULL); + assert (r->ds->ds_num == 1); - DEBUG ("db query utils: udb_result_submit: r->instance_prefix = %s;", - (r->instance_prefix == NULL) ? "NULL" : r->instance_prefix); - for (i = 0; i < r->instances_num; i++) + vl.values = &value; + vl.values_len = 1; + + value_str = column_values[r->legacy_position]; + if (0 != parse_value (value_str, &vl.values[0], r->ds->ds[0].type)) + { + ERROR ("db query utils: udb_legacy_result_handle_result: " + "Parsing `%s' as %s failed.", value_str, + DS_TYPE_TO_STRING (r->ds->ds[0].type)); + errno = EINVAL; + return (-1); + } + + sstrncpy (vl.host, q->host, sizeof (vl.host)); + sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance)); + sstrncpy (vl.type, r->type, sizeof (vl.type)); + + if (r->instance_prefix != NULL) + sstrncpy (vl.type_instance, r->instance_prefix, + sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); + + return (0); +} /* }}} int udb_legacy_result_handle_result */ + +static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */ + char **column_names, size_t column_num) +{ + if (r == NULL) + return (-EINVAL); + + assert (r->legacy_mode == 1); + + /* Make sure previous preparations are cleaned up. */ + udb_legacy_result_finish_result (r); + + if (r->legacy_position >= column_num) + { + ERROR ("db query utils: The legacy configuration specified (at least) " + "%zu `Column's, but the query returned only %zu columns!", + r->legacy_position + 1, column_num); + return (-ENOENT); + } + + /* Read `ds' and check number of values {{{ */ + r->ds = plugin_get_ds (r->type); + if (r->ds == NULL) + { + ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not " + "known by the daemon. See types.db(5) for details.", + r->type); + return (-1); + } + + if (r->ds->ds_num != 1) + { + ERROR ("db query utils: udb_result_prepare_result: The type `%s' " + "requires exactly %i values, but the legacy configuration " + "requires exactly one!", + r->type, + r->ds->ds_num); + return (-1); + } + /* }}} */ + + return (0); +} /* }}} int udb_legacy_result_prepare_result */ + +static int udb_legacy_result_create (const char *query_name, /* {{{ */ + udb_result_t **r_head, oconfig_item_t *ci, size_t position) +{ + udb_result_t *r; + + if ((ci->values_num < 1) || (ci->values_num > 2) + || (ci->values[0].type != OCONFIG_TYPE_STRING) + || ((ci->values_num == 2) + && (ci->values[1].type != OCONFIG_TYPE_STRING))) { - DEBUG ("db query utils: udb_result_submit: r->instances_buffer[%zu] = %s;", - i, r->instances_buffer[i]); + WARNING ("db query utils: The `Column' block needs either one or two " + "string arguments."); + return (-1); } + r = (udb_result_t *) malloc (sizeof (*r)); + if (r == NULL) + { + ERROR ("db query utils: malloc failed."); + return (-1); + } + memset (r, 0, sizeof (*r)); + + r->legacy_mode = 1; + r->legacy_position = position; + + r->type = strdup (ci->values[0].value.string); + if (r->type == NULL) + { + ERROR ("db query utils: strdup failed."); + free (r); + return (-1); + } + + r->instance_prefix = NULL; + if (ci->values_num == 2) + { + r->instance_prefix = strdup (ci->values[1].value.string); + if (r->instance_prefix == NULL) + { + ERROR ("db query utils: strdup failed."); + free (r->type); + free (r); + return (-1); + } + } + + /* If all went well, add this result to the list of results. */ + if (*r_head == NULL) + { + *r_head = r; + } + else + { + udb_result_t *last; + + last = *r_head; + while (last->next != NULL) + last = last->next; + + last->next = r; + } + + return (0); +} /* }}} int udb_legacy_result_create */ + +/* + * Result private functions + */ +static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */ +{ + value_list_t vl = VALUE_LIST_INIT; + size_t i; + + assert (r != NULL); + assert (r->legacy_mode == 0); + assert (r->ds != NULL); + assert (((size_t) r->ds->ds_num) == r->values_num); + vl.values = (value_t *) calloc (r->ds->ds_num, sizeof (value_t)); if (vl.values == NULL) { ERROR ("db query utils: malloc failed."); - return; + return (-1); } vl.values_len = r->ds->ds_num; for (i = 0; i < r->values_num; i++) { - char *endptr; + char *value_str = r->values_buffer[i]; - endptr = NULL; - errno = 0; - if (r->ds->ds[i].type == DS_TYPE_COUNTER) - vl.values[i].counter = (counter_t) strtoll (r->values_buffer[i], - &endptr, /* base = */ 0); - else if (r->ds->ds[i].type == DS_TYPE_GAUGE) - vl.values[i].gauge = (gauge_t) strtod (r->values_buffer[i], &endptr); - else - errno = EINVAL; - - if ((endptr == r->values_buffer[i]) || (errno != 0)) + if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i].type)) { - WARNING ("db query utils: udb_result_submit: Parsing `%s' as %s failed.", - r->values_buffer[i], - (r->ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge"); - vl.values[i].gauge = NAN; + ERROR ("db query utils: udb_result_submit: Parsing `%s' as %s failed.", + value_str, DS_TYPE_TO_STRING (r->ds->ds[i].type)); + errno = EINVAL; + return (-1); } } @@ -222,26 +378,40 @@ static void udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */ sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance)); sstrncpy (vl.type, r->type, sizeof (vl.type)); - if (r->instance_prefix == NULL) + /* Set vl.type_instance {{{ */ + if (r->instances_num <= 0) { - strjoin (vl.type_instance, sizeof (vl.type_instance), - r->instances_buffer, r->instances_num, "-"); + if (r->instance_prefix == NULL) + vl.type_instance[0] = 0; + else + sstrncpy (vl.type_instance, r->instance_prefix, + sizeof (vl.type_instance)); } - else + else /* if ((r->instances_num > 0) */ { - char tmp[DATA_MAX_NAME_LEN]; + if (r->instance_prefix == NULL) + { + strjoin (vl.type_instance, sizeof (vl.type_instance), + r->instances_buffer, r->instances_num, "-"); + } + else + { + char tmp[DATA_MAX_NAME_LEN]; - strjoin (tmp, sizeof (tmp), r->instances_buffer, r->instances_num, "-"); - tmp[sizeof (tmp) - 1] = 0; + strjoin (tmp, sizeof (tmp), r->instances_buffer, r->instances_num, "-"); + tmp[sizeof (tmp) - 1] = 0; - snprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s", - r->instance_prefix, tmp); + snprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s", + r->instance_prefix, tmp); + } } vl.type_instance[sizeof (vl.type_instance) - 1] = 0; + /* }}} */ plugin_dispatch_values (&vl); sfree (vl.values); + return (0); } /* }}} void udb_result_submit */ static void udb_result_finish_result (udb_result_t *r) /* {{{ */ @@ -249,6 +419,14 @@ static void udb_result_finish_result (udb_result_t *r) /* {{{ */ if (r == NULL) return; + if (r->legacy_mode == 1) + { + udb_legacy_result_finish_result (r); + return; + } + + assert (r->legacy_mode == 0); + r->ds = NULL; sfree (r->instances_pos); sfree (r->values_pos); @@ -261,15 +439,18 @@ static int udb_result_handle_result (udb_result_t *r, /* {{{ */ { size_t i; + if (r->legacy_mode == 1) + return (udb_legacy_result_handle_result (r, q, column_values)); + + assert (r->legacy_mode == 0); + for (i = 0; i < r->instances_num; i++) r->instances_buffer[i] = column_values[r->instances_pos[i]]; for (i = 0; i < r->values_num; i++) r->values_buffer[i] = column_values[r->values_pos[i]]; - udb_result_submit (r, q); - - return (0); + return udb_result_submit (r, q); } /* }}} int udb_result_handle_result */ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */ @@ -280,6 +461,11 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */ if (r == NULL) return (-EINVAL); + if (r->legacy_mode == 1) + return (udb_legacy_result_prepare_result (r, column_names, column_num)); + + assert (r->legacy_mode == 0); + #define BAIL_OUT(status) \ r->ds = NULL; \ sfree (r->instances_pos); \ @@ -316,12 +502,22 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */ /* Allocate r->instances_pos, r->values_pos, r->instances_buffer, and * r->values_buffer {{{ */ - r->instances_pos = (size_t *) calloc (r->instances_num, sizeof (size_t)); - if (r->instances_pos == NULL) + if (r->instances_num > 0) { - ERROR ("db query utils: udb_result_prepare_result: malloc failed."); - BAIL_OUT (-ENOMEM); - } + r->instances_pos = (size_t *) calloc (r->instances_num, sizeof (size_t)); + if (r->instances_pos == NULL) + { + ERROR ("db query utils: udb_result_prepare_result: malloc failed."); + BAIL_OUT (-ENOMEM); + } + + r->instances_buffer = (char **) calloc (r->instances_num, sizeof (char *)); + if (r->instances_buffer == NULL) + { + ERROR ("db query utils: udb_result_prepare_result: malloc failed."); + BAIL_OUT (-ENOMEM); + } + } /* if (r->instances_num > 0) */ r->values_pos = (size_t *) calloc (r->values_num, sizeof (size_t)); if (r->values_pos == NULL) @@ -330,13 +526,6 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */ BAIL_OUT (-ENOMEM); } - r->instances_buffer = (char **) calloc (r->instances_num, sizeof (char *)); - if (r->instances_buffer == NULL) - { - ERROR ("db query utils: udb_result_prepare_result: malloc failed."); - BAIL_OUT (-ENOMEM); - } - r->values_buffer = (char **) calloc (r->values_num, sizeof (char *)); if (r->values_buffer == NULL) { @@ -478,12 +667,6 @@ static int udb_result_create (const char *query_name, /* {{{ */ "result in query `%s'", query_name); status = -1; } - if (r->instances == NULL) - { - WARNING ("db query utils: `InstancesFrom' not given for " - "result in query `%s'", query_name); - status = -1; - } if (r->values == NULL) { WARNING ("db query utils: `ValuesFrom' not given for " @@ -540,7 +723,7 @@ void udb_query_free_one (udb_query_t *q) /* {{{ */ */ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */ size_t *ret_query_list_len, oconfig_item_t *ci, - udb_query_create_callback_t cb) + udb_query_create_callback_t cb, int legacy_mode) { udb_query_t **query_list; size_t query_list_len; @@ -549,6 +732,8 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */ int status; int i; + size_t legacy_position; + if ((ret_query_list == NULL) || (ret_query_list_len == NULL)) return (-EINVAL); query_list = *ret_query_list; @@ -569,9 +754,12 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */ return (-1); } memset (q, 0, sizeof (*q)); + q->legacy_mode = legacy_mode; q->min_version = 0; q->max_version = UINT_MAX; + legacy_position = 0; + status = udb_config_set_string (&q->name, ci); if (status != 0) { @@ -592,21 +780,44 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */ status = udb_config_set_uint (&q->min_version, child); else if (strcasecmp ("MaxVersion", child->key) == 0) status = udb_config_set_uint (&q->max_version, child); + /* PostgreSQL compatibility code */ - else if (strcasecmp ("MinPGVersion", child->key) == 0) + else if ((strcasecmp ("Query", child->key) == 0) + && (q->legacy_mode == 1)) + { + WARNING ("db query utils: Query `%s': The `Query' option is " + "deprecated. Please use `Statement' instead.", + q->name); + status = udb_config_set_string (&q->statement, child); + } + else if ((strcasecmp ("Column", child->key) == 0) + && (q->legacy_mode == 1)) + { + WARNING ("db query utils: Query `%s': The `Column' option is " + "deprecated. Please use the new syntax instead.", + q->name); + status = udb_legacy_result_create (q->name, &q->results, child, + legacy_position); + legacy_position++; + } + else if ((strcasecmp ("MinPGVersion", child->key) == 0) + && (q->legacy_mode == 1)) { WARNING ("db query utils: Query `%s': The `MinPGVersion' option is " "deprecated. Please use `MinVersion' instead.", q->name); status = udb_config_set_uint (&q->min_version, child); } - else if (strcasecmp ("MaxPGVersion", child->key) == 0) + else if ((strcasecmp ("MaxPGVersion", child->key) == 0) + && (q->legacy_mode == 1)) { WARNING ("db query utils: Query `%s': The `MaxPGVersion' option is " "deprecated. Please use `MaxVersion' instead.", q->name); status = udb_config_set_uint (&q->max_version, child); } + + /* Call custom callbacks */ else if (cb != NULL) { status = (*cb) (q, child); @@ -689,63 +900,90 @@ void udb_query_free (udb_query_t **query_list, size_t query_list_len) /* {{{ */ sfree (query_list); } /* }}} void udb_query_free */ -int udb_query_pick_from_list (oconfig_item_t *ci, /* {{{ */ +int udb_query_pick_from_list_by_name (const char *name, /* {{{ */ udb_query_t **src_list, size_t src_list_len, udb_query_t ***dst_list, size_t *dst_list_len) { - const char *name; - udb_query_t *q; - udb_query_t **tmp_list; - size_t tmp_list_len; size_t i; + int num_added; - if ((ci == NULL) || (src_list == NULL) || (dst_list == NULL) + if ((name == NULL) || (src_list == NULL) || (dst_list == NULL) || (dst_list_len == NULL)) { - ERROR ("db query utils: Invalid argument."); + ERROR ("db query utils: udb_query_pick_from_list_by_name: " + "Invalid argument."); return (-EINVAL); } - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) + num_added = 0; + for (i = 0; i < src_list_len; i++) { - ERROR ("db query utils: The `%s' config option " - "needs exactly one string argument.", ci->key); - return (-1); - } - name = ci->values[0].value.string; + udb_query_t **tmp_list; + size_t tmp_list_len; - q = NULL; - for (i = 0; i < src_list_len; i++) - if (strcasecmp (name, src_list[i]->name) == 0) + if (strcasecmp (name, src_list[i]->name) != 0) + continue; + + tmp_list_len = *dst_list_len; + tmp_list = (udb_query_t **) realloc (*dst_list, (tmp_list_len + 1) + * sizeof (udb_query_t *)); + if (tmp_list == NULL) { - q = src_list[i]; - break; + ERROR ("db query utils: realloc failed."); + return (-ENOMEM); } - if (q == NULL) + tmp_list[tmp_list_len] = src_list[i]; + tmp_list_len++; + + *dst_list = tmp_list; + *dst_list_len = tmp_list_len; + + num_added++; + } /* for (i = 0; i < src_list_len; i++) */ + + if (num_added <= 0) { - ERROR ("db query utils: Cannot find query `%s'. Make sure the <%s> " + ERROR ("db query utils: Cannot find query `%s'. Make sure the " "block is above the database definition!", - name, ci->key); + name); return (-ENOENT); } + else + { + DEBUG ("db query utils: Added %i versions of query `%s'.", + num_added, name); + } - tmp_list_len = *dst_list_len; - tmp_list = (udb_query_t **) realloc (*dst_list, (tmp_list_len + 1) - * sizeof (udb_query_t *)); - if (tmp_list == NULL) + return (0); +} /* }}} int udb_query_pick_from_list_by_name */ + +int udb_query_pick_from_list (oconfig_item_t *ci, /* {{{ */ + udb_query_t **src_list, size_t src_list_len, + udb_query_t ***dst_list, size_t *dst_list_len) +{ + const char *name; + + if ((ci == NULL) || (src_list == NULL) || (dst_list == NULL) + || (dst_list_len == NULL)) { - ERROR ("db query utils: realloc failed."); - return (-ENOMEM); + ERROR ("db query utils: udb_query_pick_from_list: " + "Invalid argument."); + return (-EINVAL); } - tmp_list[tmp_list_len] = q; - tmp_list_len++; - *dst_list = tmp_list; - *dst_list_len = tmp_list_len; + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + ERROR ("db query utils: The `%s' config option " + "needs exactly one string argument.", ci->key); + return (-1); + } + name = ci->values[0].value.string; - return (0); + return (udb_query_pick_from_list_by_name (name, + src_list, src_list_len, + dst_list, dst_list_len)); } /* }}} int udb_query_pick_from_list */ const char *udb_query_get_name (udb_query_t *q) /* {{{ */