From: Sebastian Harl Date: Fri, 15 May 2015 20:53:44 +0000 (+0200) Subject: Merged branch 'master' of git://git.tokkee.org/sysdb. X-Git-Tag: sysdb-0.8.0~94 X-Git-Url: https://git.tokkee.org/?p=sysdb.git;a=commitdiff_plain;h=c2f7813397319e930c81c4f64b213bec64549167;hp=32a9483322ca1462e9be6889f053d6ea3ed1e9df Merged branch 'master' of git://git.tokkee.org/sysdb. --- diff --git a/src/Makefile.am b/src/Makefile.am index ed81316..6b560df 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,6 +81,7 @@ libsysdb_la_SOURCES = \ core/store_expr.c \ core/store_json.c \ core/store_lookup.c \ + core/store_query.c \ core/data.c include/core/data.h \ core/time.c include/core/time.h \ core/timeseries.c include/core/timeseries.h \ diff --git a/src/core/store-private.h b/src/core/store-private.h index 874dc05..060ec7b 100644 --- a/src/core/store-private.h +++ b/src/core/store-private.h @@ -163,6 +163,9 @@ enum { MATCHER_GT, MATCHER_REGEX, MATCHER_NREGEX, + + /* a generic query */ + MATCHER_QUERY, }; #define MATCHER_SYM(t) \ @@ -183,6 +186,7 @@ enum { : ((t) == MATCHER_GT) ? ">" \ : ((t) == MATCHER_REGEX) ? "=~" \ : ((t) == MATCHER_NREGEX) ? "!~" \ + : ((t) == MATCHER_QUERY) ? "QUERY" \ : "UNKNOWN") /* matcher base type */ @@ -236,6 +240,14 @@ typedef struct { } isnull_matcher_t; #define ISNULL_M(m) ((isnull_matcher_t *)(m)) +typedef struct { + sdb_store_matcher_t super; + sdb_ast_node_t *ast; + sdb_store_matcher_t *matcher; + sdb_store_matcher_t *filter; +} query_matcher_t; +#define QUERY_M(m) ((query_matcher_t *)(m)) + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/core/store.c b/src/core/store.c index ea30da9..eb58ad8 100644 --- a/src/core/store.c +++ b/src/core/store.c @@ -850,6 +850,8 @@ sdb_store_fetch_timeseries(const char *hostname, const char *metric, sdb_timeseries_t *ts; + int status = 0; + if ((! hostname) || (! metric) || (! opts) || (! buf)) return -1; @@ -876,6 +878,7 @@ sdb_store_fetch_timeseries(const char *hostname, const char *metric, sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' " "- no data-store configured for the stored metric", hostname, metric); + sdb_object_deref(SDB_OBJ(m)); pthread_rwlock_unlock(&host_lock); return -1; } @@ -893,13 +896,14 @@ sdb_store_fetch_timeseries(const char *hostname, const char *metric, sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' " "- %s fetcher callback returned no data for '%s'", hostname, metric, type, id); - return -1; + status = -1; } } ts_tojson(ts, buf); + sdb_object_deref(SDB_OBJ(m)); sdb_timeseries_destroy(ts); - return 0; + return status; } /* sdb_store_fetch_timeseries */ int diff --git a/src/core/store_lookup.c b/src/core/store_lookup.c index 18a0235..4862905 100644 --- a/src/core/store_lookup.c +++ b/src/core/store_lookup.c @@ -352,6 +352,8 @@ matchers[] = { match_cmp, match_regex, match_regex, + + NULL, /* QUERY */ }; /* @@ -744,6 +746,8 @@ sdb_store_matcher_matches(sdb_store_matcher_t *m, sdb_store_obj_t *obj, if ((m->type < 0) || ((size_t)m->type >= SDB_STATIC_ARRAY_LEN(matchers))) return 0; + if (! matchers[m->type]) + return 0; return matchers[m->type](m, obj, filter); } /* sdb_store_matcher_matches */ diff --git a/src/core/store_query.c b/src/core/store_query.c new file mode 100644 index 0000000..90d45bf --- /dev/null +++ b/src/core/store_query.c @@ -0,0 +1,344 @@ +/* + * SysDB - src/core/store_lookup.c + * Copyright (C) 2014-2015 Sebastian 'tokkee' Harl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core/object.h" +#include "core/store-private.h" +#include "parser/ast.h" +#include "utils/error.h" + +#include + +static sdb_store_matcher_t * +node_to_matcher(sdb_ast_node_t *n); + +static sdb_store_expr_t * +node_to_expr(sdb_ast_node_t *n) +{ + sdb_store_expr_t *left = NULL, *right = NULL; + sdb_store_expr_t *e; + int op; + + if (! n) { + sdb_log(SDB_LOG_ERR, "store: Encountered empty AST expression node"); + return NULL; + } + + switch (n->type) { + case SDB_AST_TYPE_OPERATOR: + if (! SDB_AST_IS_ARITHMETIC(n)) { + sdb_log(SDB_LOG_ERR, "store: Invalid arithmetic operator of " + "type %s (%#x)", SDB_AST_TYPE_TO_STRING(n), n->type); + return NULL; + } + + left = node_to_expr(SDB_AST_OP(n)->left); + if (! left) + return NULL; + right = node_to_expr(SDB_AST_OP(n)->right); + if (! right) { + sdb_object_deref(SDB_OBJ(left)); + return NULL; + } + op = SDB_AST_OP_TO_DATA_OP(SDB_AST_OP(n)->kind); + e = sdb_store_expr_create(op, left, right); + break; + + case SDB_AST_TYPE_CONST: + return sdb_store_expr_constvalue(&SDB_AST_CONST(n)->value); + + case SDB_AST_TYPE_VALUE: + if (SDB_AST_VALUE(n)->type == SDB_ATTRIBUTE) + return sdb_store_expr_attrvalue(SDB_AST_VALUE(n)->name); + return sdb_store_expr_fieldvalue(SDB_AST_VALUE(n)->type); + + case SDB_AST_TYPE_TYPED: + right = node_to_expr(SDB_AST_TYPED(n)->expr); + if (! right) + return NULL; + e = sdb_store_expr_typed(SDB_AST_TYPED(n)->type, right); + break; + + default: + sdb_log(SDB_LOG_ERR, "store: Invalid matcher node of type %s (%#x)", + SDB_AST_TYPE_TO_STRING(n), n->type); + e = NULL; + } + + /* expressions take a reference */ + sdb_object_deref(SDB_OBJ(left)); + sdb_object_deref(SDB_OBJ(right)); + return e; +} /* node_to_expr */ + +static sdb_store_matcher_t * +logical_to_matcher(sdb_ast_node_t *n) +{ + sdb_store_matcher_t *left = NULL, *right; + sdb_store_matcher_t *m; + + if (SDB_AST_OP(n)->left) { + left = node_to_matcher(SDB_AST_OP(n)->left); + if (! left) + return NULL; + } + right = node_to_matcher(SDB_AST_OP(n)->right); + if (! right) { + sdb_object_deref(SDB_OBJ(left)); + return NULL; + } + + switch (SDB_AST_OP(n)->kind) { + case SDB_AST_AND: + m = sdb_store_con_matcher(left, right); + break; + case SDB_AST_OR: + m = sdb_store_dis_matcher(left, right); + break; + case SDB_AST_NOT: + m = sdb_store_inv_matcher(right); + break; + + default: + m = NULL; + } + + /* matchers take a reference */ + sdb_object_deref(SDB_OBJ(left)); + sdb_object_deref(SDB_OBJ(right)); + return m; +} /* logical_to_matcher */ + +static sdb_store_matcher_t * +cmp_to_matcher(sdb_ast_node_t *n) +{ + sdb_store_expr_t *left = NULL, *right; + sdb_store_matcher_t *m; + + if (SDB_AST_OP(n)->left) { + left = node_to_expr(SDB_AST_OP(n)->left); + if (! left) + return NULL; + } + right = node_to_expr(SDB_AST_OP(n)->right); + if (! right) { + sdb_object_deref(SDB_OBJ(left)); + return NULL; + } + + switch (SDB_AST_OP(n)->kind) { + case SDB_AST_LT: + m = sdb_store_lt_matcher(left, right); + break; + case SDB_AST_LE: + m = sdb_store_le_matcher(left, right); + break; + case SDB_AST_EQ: + m = sdb_store_eq_matcher(left, right); + break; + case SDB_AST_NE: + m = sdb_store_ne_matcher(left, right); + break; + case SDB_AST_GE: + m = sdb_store_ge_matcher(left, right); + break; + case SDB_AST_GT: + m = sdb_store_gt_matcher(left, right); + break; + case SDB_AST_REGEX: + m = sdb_store_regex_matcher(left, right); + break; + case SDB_AST_NREGEX: + m = sdb_store_nregex_matcher(left, right); + break; + case SDB_AST_ISNULL: + m = sdb_store_isnull_matcher(right); + break; + case SDB_AST_IN: + m = sdb_store_in_matcher(left, right); + break; + + default: + sdb_log(SDB_LOG_ERR, "store: Invalid matcher node of type %s (%#x)", + SDB_AST_TYPE_TO_STRING(n), n->type); + m = NULL; + } + + /* matchers take a reference */ + sdb_object_deref(SDB_OBJ(left)); + sdb_object_deref(SDB_OBJ(right)); + return m; +} /* cmp_to_matcher */ + +static sdb_store_matcher_t * +iter_to_matcher(sdb_ast_node_t *n) +{ + sdb_store_expr_t *iter; + sdb_store_matcher_t *expr, *m; + + assert((SDB_AST_ITER(n)->expr->type == SDB_AST_TYPE_OPERATOR) + && (! SDB_AST_OP(SDB_AST_ITER(n)->expr)->left)); + + iter = node_to_expr(SDB_AST_ITER(n)->iter); + if (! iter) + return NULL; + expr = cmp_to_matcher(SDB_AST_ITER(n)->expr); + if (! expr) { + sdb_object_deref(SDB_OBJ(iter)); + return NULL; + } + + switch (SDB_AST_ITER(n)->kind) { + case SDB_AST_ALL: + m = sdb_store_all_matcher(iter, expr); + break; + case SDB_AST_ANY: + m = sdb_store_any_matcher(iter, expr); + break; + + default: + sdb_log(SDB_LOG_ERR, "store: Invalid iterator node of type %s (%#x)", + SDB_AST_OP_TO_STRING(SDB_AST_ITER(n)->kind), SDB_AST_ITER(n)->kind); + m = NULL; + } + + /* matchers take a reference */ + sdb_object_deref(SDB_OBJ(iter)); + sdb_object_deref(SDB_OBJ(expr)); + return m; +} /* iter_to_matcher */ + +static sdb_store_matcher_t * +node_to_matcher(sdb_ast_node_t *n) +{ + int kind; + + if (! n) { + sdb_log(SDB_LOG_ERR, "store: Encountered empty AST matcher node"); + return NULL; + } + + switch (n->type) { + case SDB_AST_TYPE_OPERATOR: + if (! SDB_AST_IS_LOGICAL(n)) { + sdb_log(SDB_LOG_ERR, "store: Invalid logical operator of " + "type %s (%#x)", SDB_AST_TYPE_TO_STRING(n), n->type); + return NULL; + } + + kind = SDB_AST_OP(n)->kind; + if ((kind == SDB_AST_AND) || (kind == SDB_AST_OR) || (kind == SDB_AST_NOT)) + return logical_to_matcher(n); + else + return cmp_to_matcher(n); + + case SDB_AST_TYPE_ITERATOR: + return iter_to_matcher(n); + } + + sdb_log(SDB_LOG_ERR, "store: Invalid matcher node of type %s (%#x)", + SDB_AST_TYPE_TO_STRING(n), n->type); + return NULL; +} /* node_to_matcher */ + +/* + * matcher type + */ + +static int +query_matcher_init(sdb_object_t *obj, va_list ap) +{ + sdb_ast_node_t *ast = va_arg(ap, sdb_ast_node_t *); + sdb_ast_node_t *matcher = NULL, *filter = NULL; + + M(obj)->type = MATCHER_QUERY; + + QUERY_M(obj)->ast = ast; + sdb_object_ref(SDB_OBJ(ast)); + + switch (ast->type) { + case SDB_AST_TYPE_FETCH: + filter = SDB_AST_FETCH(ast)->filter; + break; + case SDB_AST_TYPE_LIST: + filter = SDB_AST_LIST(ast)->filter; + break; + case SDB_AST_TYPE_LOOKUP: + matcher = SDB_AST_LOOKUP(ast)->matcher; + filter = SDB_AST_LOOKUP(ast)->filter; + break; + case SDB_AST_TYPE_STORE: + case SDB_AST_TYPE_TIMESERIES: + /* nothing to do */ + break; + + default: + sdb_log(SDB_LOG_ERR, "store: Invalid top-level AST node " + "of type %#x", ast->type); + return -1; + } + + if (matcher) { + QUERY_M(obj)->matcher = node_to_matcher(matcher); + if (! QUERY_M(obj)->matcher) + return -1; + } + if (filter) { + QUERY_M(obj)->filter = node_to_matcher(filter); + if (! QUERY_M(obj)->filter) + return -1; + } + + return 0; +} /* query_matcher_init */ + +static void +query_matcher_destroy(sdb_object_t *obj) +{ + sdb_object_deref(SDB_OBJ(QUERY_M(obj)->ast)); + sdb_object_deref(SDB_OBJ(QUERY_M(obj)->matcher)); + sdb_object_deref(SDB_OBJ(QUERY_M(obj)->filter)); +} /* query_matcher_destroy */ + +static sdb_type_t query_type = { + /* size = */ sizeof(query_matcher_t), + /* init = */ query_matcher_init, + /* destroy = */ query_matcher_destroy, +}; + +/* + * public API + */ + +sdb_store_matcher_t * +sdb_store_query_prepare(sdb_ast_node_t *ast) +{ + if (! ast) + return NULL; + return M(sdb_object_create(SDB_AST_TYPE_TO_STRING(ast), query_type, ast)); +} /* sdb_store_query_prepare */ + +/* vim: set tw=78 sw=4 ts=4 noexpandtab : */ diff --git a/src/frontend/analyzer.c b/src/frontend/analyzer.c index 4bd3702..01e1ad3 100644 --- a/src/frontend/analyzer.c +++ b/src/frontend/analyzer.c @@ -95,7 +95,8 @@ analyze_expr(int context, sdb_store_expr_t *e, sdb_strbuf_t *errbuf) if (context == (int)e->data.data.integer) return 0; if ((e->data.data.integer == SDB_HOST) && - ((context == SDB_SERVICE) || (context == SDB_METRIC))) + ((context == SDB_SERVICE) || (context == SDB_METRIC) + || (context < 0))) return 0; sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s " "in %s context", diff --git a/src/frontend/store.c b/src/frontend/store.c index ff56316..6cad8cd 100644 --- a/src/frontend/store.c +++ b/src/frontend/store.c @@ -162,7 +162,7 @@ sdb_fe_store_service(sdb_conn_t *conn, const sdb_proto_service_t *svc) if ((! conn) || (! svc) || (! svc->hostname) || (! svc->name)) return -1; - snprintf(name, sizeof(name), svc->hostname, svc->name); + snprintf(name, sizeof(name), "%s.%s", svc->hostname, svc->name); return store_reply(conn, SDB_SERVICE, name, sdb_store_service(svc->hostname, svc->name, svc->last_update)); } /* sdb_fe_store_service */ @@ -178,7 +178,7 @@ sdb_fe_store_metric(sdb_conn_t *conn, const sdb_proto_metric_t *metric) store.type = metric->store_type; store.id = metric->store_id; - snprintf(name, sizeof(name), metric->hostname, metric->name); + snprintf(name, sizeof(name), "%s.%s", metric->hostname, metric->name); return store_reply(conn, SDB_METRIC, name, sdb_store_metric(metric->hostname, metric->name, &store, metric->last_update)); diff --git a/src/include/core/store.h b/src/include/core/store.h index 94a5713..0abf0cb 100644 --- a/src/include/core/store.h +++ b/src/include/core/store.h @@ -33,6 +33,7 @@ #include "core/data.h" #include "core/time.h" #include "core/timeseries.h" +#include "parser/ast.h" #include "utils/strbuf.h" #include @@ -359,6 +360,17 @@ int sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res, sdb_store_matcher_t *filter); +/* + * sdb_store_query_prepare: + * Prepare the query described by 'ast' for execution in a store. + * + * Returns: + * - a store matcher on success + * - NULL else + */ +sdb_store_matcher_t * +sdb_store_query_prepare(sdb_ast_node_t *ast); + /* * sdb_store_expr_create: * Creates an arithmetic expression implementing the specified operator on the diff --git a/src/parser/analyzer.c b/src/parser/analyzer.c index 01539ed..e03cbdb 100644 --- a/src/parser/analyzer.c +++ b/src/parser/analyzer.c @@ -347,7 +347,7 @@ analyze_typed(int context, sdb_ast_typed_t *t, sdb_strbuf_t *errbuf) /* self-references are allowed and services and metrics may reference * their parent host; everything may reference attributes */ - if ((context != t->type) && (context != UNSPEC_CONTEXT) + if ((context != t->type) && (context > 0) && (((context != SDB_SERVICE) && (context != SDB_METRIC)) || (t->type != SDB_HOST)) && (t->type != SDB_ATTRIBUTE)) { diff --git a/t/coverage.sh b/t/coverage.sh index d6f00be..d896156 100755 --- a/t/coverage.sh +++ b/t/coverage.sh @@ -49,7 +49,7 @@ touch configure.ac && make configure make lcov --base-directory src --directory src --zerocount -make -j25 test || (status=$?; cat t/test-suite.log 2> /dev/null; exit $status) +make -j10 test || (status=$?; cat t/test-suite.log 2> /dev/null; exit $status) # old versions of lcov don't support --no-external lcov --base-directory src --directory src --no-external \ diff --git a/t/unit/frontend/query_test.c b/t/unit/frontend/query_test.c index 73f3042..7e15502 100644 --- a/t/unit/frontend/query_test.c +++ b/t/unit/frontend/query_test.c @@ -104,6 +104,13 @@ populate(void) "{\"name\": \"hostname\", \"value\": \"h1\", " \ "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \ "\"update_interval\": \"0s\", \"backends\": []}]}]}" +#define HOST_H1_ARRAY "["HOST_H1"]" +#define HOST_H1_LISTING \ + "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": []}" +#define HOST_H2_LISTING \ + "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": []}" #define SERVICE_H2_S1 \ "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \ @@ -115,6 +122,15 @@ populate(void) "{\"name\": \"hostname\", \"value\": \"h2\", " \ "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \ "\"update_interval\": \"0s\", \"backends\": []}]}]}" +#define SERVICE_H2_S1_ARRAY "["SERVICE_H2_S1"]" +#define SERVICE_H2_S12_LISTING \ + "[{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": [], " \ + "\"services\": [" \ + "{\"name\": \"s1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": []}," \ + "{\"name\": \"s2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": []}]}]" #define METRIC_H1_M1 \ "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \ @@ -130,6 +146,34 @@ populate(void) "{\"name\": \"k3\", \"value\": 42, " \ "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \ "\"update_interval\": \"0s\", \"backends\": []}]}]}" +#define METRIC_H12_M1_ARRAY \ + "["METRIC_H1_M1"," \ + "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": [], " \ + "\"metrics\": [" \ + "{\"name\": \"m1\", \"timeseries\": false, " \ + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": [], " \ + "\"attributes\": [" \ + "{\"name\": \"hostname\", \"value\": \"h2\", " \ + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": []}]}]}]" +#define METRIC_H12_M12_LISTING \ + "[{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": [], " \ + "\"metrics\": [" \ + "{\"name\": \"m1\", \"timeseries\": false, " \ + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": []}," \ + "{\"name\": \"m2\", \"timeseries\": false, " \ + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": []}]}," \ + "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": [], " \ + "\"metrics\": [" \ + "{\"name\": \"m1\", \"timeseries\": false, " \ + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \ + "\"update_interval\": \"0s\", \"backends\": []}]}]" typedef struct { sdb_conn_t conn; @@ -240,101 +284,253 @@ fail_if_strneq(const char *got, const char *expected, size_t n, const char *fmt, */ static struct { - int type; - const char *hostname; - const char *name; - const char *filter; - + const char *query; int expected; uint32_t code; size_t len; + uint32_t type; const char *data; -} exec_fetch_data[] = { +} query_data[] = { /* hosts */ { - SDB_HOST, "h1", NULL, NULL, - 0, SDB_CONNECTION_DATA, 1110, HOST_H1, + "LIST hosts", + 0, SDB_CONNECTION_DATA, 205, SDB_CONNECTION_LIST, + "["HOST_H1_LISTING","HOST_H2_LISTING"]", }, { - SDB_HOST, "h1", NULL, "age >= 0s", /* always matches */ - 0, SDB_CONNECTION_DATA, 1110, HOST_H1, + "LIST hosts; LIST hosts", /* ignore second (and later) commands */ + 0, SDB_CONNECTION_DATA, 205, SDB_CONNECTION_LIST, + "["HOST_H1_LISTING","HOST_H2_LISTING"]", }, { - SDB_HOST, "h1", NULL, "age < 0s", /* never matches */ - -1, UINT32_MAX, 0, NULL, /* FETCH fails if the object doesn't exist */ + "LIST hosts FILTER name = 'h1'", + 0, SDB_CONNECTION_DATA, 105, SDB_CONNECTION_LIST, "["HOST_H1_LISTING"]", }, { - SDB_HOST, "x1", NULL, NULL, /* does not exist */ - -1, UINT32_MAX, 0, NULL, + "LIST hosts FILTER name = 's1'", + 0, SDB_CONNECTION_DATA, 6, SDB_CONNECTION_LIST, "[]", }, { - SDB_HOST, "h1", "s1", NULL, /* invalid args */ - -1, UINT32_MAX, 0, NULL, + "FETCH host 'h1'", + 0, SDB_CONNECTION_DATA, 1110, SDB_CONNECTION_FETCH, HOST_H1, + }, + { + "LOOKUP hosts MATCHING name = 'h1'", + 0, SDB_CONNECTION_DATA, 1112, SDB_CONNECTION_LOOKUP, HOST_H1_ARRAY, + }, + { + "FETCH host 'h1' FILTER age >= 0s", /* always matches */ + 0, SDB_CONNECTION_DATA, 1110, SDB_CONNECTION_FETCH, HOST_H1, + }, + { + "LOOKUP hosts MATCHING name = 'h1' FILTER age >= 0s", /* always matches */ + 0, SDB_CONNECTION_DATA, 1112, SDB_CONNECTION_LOOKUP, HOST_H1_ARRAY, + }, + { + "FETCH host 'h1' FILTER age < 0s", /* never matches */ + -1, UINT32_MAX, 0, 0, NULL, /* FETCH fails if the object doesn't exist */ + }, + { + "LOOKUP hosts MATCHING name = 'h1' FILTER age < 0s", /* never matches */ + 0, SDB_CONNECTION_DATA, 6, SDB_CONNECTION_LOOKUP, "[]", + }, + { + "FETCH host 'x1'", /* does not exist */ + -1, UINT32_MAX, 0, 0, NULL, + }, + { + "LOOKUP hosts MATCHING name = 'x1'", /* does not exist */ + 0, SDB_CONNECTION_DATA, 6, SDB_CONNECTION_LOOKUP, "[]", + }, + { + "FETCH host 'h1'.'s1'", /* invalid args */ + -1, UINT32_MAX, 0, 0, NULL, + }, + { + "LOOKUP hosts BY name = 'x1'", /* does not exist */ + -1, UINT32_MAX, 0, 0, NULL, }, /* services */ { - SDB_SERVICE, "h2", "s1", NULL, - 0, SDB_CONNECTION_DATA, 356, SERVICE_H2_S1, + "LIST services", + 0, SDB_CONNECTION_DATA, 320, SDB_CONNECTION_LIST, SERVICE_H2_S12_LISTING, + }, + { + "LIST services FILTER host.name = 'h2'", + 0, SDB_CONNECTION_DATA, 320, SDB_CONNECTION_LIST, SERVICE_H2_S12_LISTING, + }, + { + "LIST services FILTER host.name = 'h1'", + 0, SDB_CONNECTION_DATA, 6, SDB_CONNECTION_LIST, "[]", + }, + { + "FETCH service 'h2'.'s1'", + 0, SDB_CONNECTION_DATA, 356, SDB_CONNECTION_FETCH, SERVICE_H2_S1, + }, + { + "LOOKUP services MATCHING name = 's1'", + 0, SDB_CONNECTION_DATA, 358, SDB_CONNECTION_LOOKUP, SERVICE_H2_S1_ARRAY, + }, + { + "FETCH service 'h2'.'s1' FILTER age >= 0s", /* always matches */ + 0, SDB_CONNECTION_DATA, 356, SDB_CONNECTION_FETCH, SERVICE_H2_S1, + }, + { + "LOOKUP services MATCHING name = 's1' FILTER age >= 0s", /* always matches */ + 0, SDB_CONNECTION_DATA, 358, SDB_CONNECTION_LOOKUP, SERVICE_H2_S1_ARRAY, }, { - SDB_SERVICE, "h2", "s1", "age >= 0s", /* always matches */ - 0, SDB_CONNECTION_DATA, 356, SERVICE_H2_S1, + "FETCH service 'h2'.'s1' FILTER age < 0s", /* never matches */ + -1, UINT32_MAX, 0, 0, NULL, }, { - SDB_SERVICE, "h2", "s1", "age < 0s", /* never matches */ - -1, UINT32_MAX, 0, NULL, + "LOOKUP services MATCHING name = 's1' FILTER age < 0s", /* never matches */ + 0, SDB_CONNECTION_DATA, 6, SDB_CONNECTION_LOOKUP, "[]", }, { - SDB_SERVICE, "h2", "s1", "name = 'h2'", /* only matches host */ - -1, UINT32_MAX, 0, NULL, + "FETCH service 'h2'.'s1' FILTER name = 'h2'", /* only matches host */ + -1, UINT32_MAX, 0, 0, NULL, }, { - SDB_SERVICE, "h2", "x1", NULL, /* does not exist */ - -1, UINT32_MAX, 0, NULL, + "LOOKUP services MATCHING name = 's1' FILTER name = 'h2'", /* only matches host */ + 0, SDB_CONNECTION_DATA, 6, SDB_CONNECTION_LOOKUP, "[]", }, { - SDB_SERVICE, "x2", "s1", NULL, /* does not exist */ - -1, UINT32_MAX, 0, NULL, + "FETCH service 'h2'.'x1'", /* does not exist */ + -1, UINT32_MAX, 0, 0, NULL, }, { - SDB_SERVICE, "h2", NULL, NULL, /* invalid args */ - -1, UINT32_MAX, 0, NULL, + "FETCH service 'x2'.'s1'", /* does not exist */ + -1, UINT32_MAX, 0, 0, NULL, + }, + { + "FETCH service 'h2'", /* invalid args */ + -1, UINT32_MAX, 0, 0, NULL, }, /* metrics */ { - SDB_METRIC, "h1", "m1", NULL, - 0, SDB_CONNECTION_DATA, 489, METRIC_H1_M1, + "LIST metrics", + 0, SDB_CONNECTION_DATA, 596, SDB_CONNECTION_LIST, METRIC_H12_M12_LISTING, + }, + { + "LIST metrics FILTER age > 0s", + 0, SDB_CONNECTION_DATA, 596, SDB_CONNECTION_LIST, METRIC_H12_M12_LISTING, + }, + { + "LIST metrics FILTER age < 0s", + 0, SDB_CONNECTION_DATA, 6, SDB_CONNECTION_LIST, "[]", + }, + { + "FETCH metric 'h1'.'m1'", + 0, SDB_CONNECTION_DATA, 489, SDB_CONNECTION_FETCH, METRIC_H1_M1, + }, + { + "LOOKUP metrics MATCHING name = 'm1'", + 0, SDB_CONNECTION_DATA, 864, SDB_CONNECTION_LOOKUP, METRIC_H12_M1_ARRAY, + }, + { + "FETCH metric 'h1'.'m1' FILTER age >= 0s", /* always matches */ + 0, SDB_CONNECTION_DATA, 489, SDB_CONNECTION_FETCH, METRIC_H1_M1, + }, + { + "LOOKUP metrics MATCHING name = 'm1' FILTER age >= 0s", /* always matches */ + 0, SDB_CONNECTION_DATA, 864, SDB_CONNECTION_LOOKUP, METRIC_H12_M1_ARRAY, + }, + { + "FETCH metric 'h1'.'m1' FILTER age < 0s", /* never matches */ + -1, UINT32_MAX, 0, 0, NULL, + }, + { + "LOOKUP metrics MATCHING name = 'm1' FILTER age < 0s", /* never matches */ + 0, SDB_CONNECTION_DATA, 6, SDB_CONNECTION_LOOKUP, "[]", }, { - SDB_METRIC, "h1", "m1", "age >= 0s", /* always matches */ - 0, SDB_CONNECTION_DATA, 489, METRIC_H1_M1, + "FETCH metric 'h1'.'m1' FILTER name = 'h1'", /* only matches host */ + -1, UINT32_MAX, 0, 0, NULL, }, { - SDB_METRIC, "h1", "m1", "age < 0s", /* never matches */ - -1, UINT32_MAX, 0, NULL, + "LOOKUP metrics MATCHING name = 'm1' FILTER name = 'h1'", /* only matches host */ + 0, SDB_CONNECTION_DATA, 6, SDB_CONNECTION_LOOKUP, "[]", }, { - SDB_METRIC, "h1", "m1", "name = 'h1'", /* only matches host */ - -1, UINT32_MAX, 0, NULL, + "FETCH metric 'h1'.'x1'", /* does not exist */ + -1, UINT32_MAX, 0, 0, NULL, }, { - SDB_METRIC, "h1", "x1", NULL, /* does not exist */ - -1, UINT32_MAX, 0, NULL, + "FETCH metric 'x1'.'m1'", /* does not exist */ + -1, UINT32_MAX, 0, 0, NULL, }, { - SDB_METRIC, "x1", "m1", NULL, /* does not exist */ - -1, UINT32_MAX, 0, NULL, + "FETCH metric 'x1'", /* invalid args */ + -1, UINT32_MAX, 0, 0, NULL, }, + /* timeseries */ { - SDB_METRIC, "x1", NULL, NULL, /* invalid args */ - -1, UINT32_MAX, 0, NULL, + "TIMESERIES 'h1'.'m1'", + -1, UINT32_MAX, 0, 0, NULL, /* no data-store available */ + }, + { + "TIMESERIES 'h1'.'x1'", + -1, UINT32_MAX, 0, 0, NULL, /* does not exist */ + }, + { + "TIMESERIES 'x1'.'m1'", + -1, UINT32_MAX, 0, 0, NULL, /* does not exist */ + }, + /* store commands */ + { + "STORE host 'hA' LAST UPDATE 01:00", + 0, SDB_CONNECTION_OK, 27, 0, "Successfully stored host hA", + }, + { + "STORE host 'hA'", + 0, SDB_CONNECTION_OK, 27, 0, "Successfully stored host hA", + }, + { + "STORE host attribute 'h1'.'aA' 'vA'", + 0, SDB_CONNECTION_OK, 40, 0, "Successfully stored host attribute h1.aA", + }, + { + "STORE host attribute 'x1'.'aA' 'vA'", + -1, UINT32_MAX, 0, 0, NULL, + }, + { + "STORE service 'h1'.'sA'", + 0, SDB_CONNECTION_OK, 33, 0, "Successfully stored service h1.sA", + }, + { + "STORE service 'x1'.'sA'", + -1, UINT32_MAX, 0, 0, NULL, + }, + { + "STORE service attribute 'h2'.'s1'.'aA' 'vA'", + 0, SDB_CONNECTION_OK, 43, 0, "Successfully stored service attribute s1.aA", + }, + { + "STORE service attribute 'h2'.'x1'.'aA' 'vA'", + -1, UINT32_MAX, 0, 0, NULL, + }, + { + "STORE metric 'h1'.'mA'", + 0, SDB_CONNECTION_OK, 32, 0, "Successfully stored metric h1.mA", + }, + { + "STORE metric 'x1'.'mA'", + -1, UINT32_MAX, 0, 0, NULL, + }, + { + "STORE metric attribute 'h1'.'m1'.'aA' 'vA'", + 0, SDB_CONNECTION_OK, 42, 0, "Successfully stored metric attribute m1.aA", + }, + { + "STORE metric attribute 'h1'.'x1'.'aA' 'vA'", + -1, UINT32_MAX, 0, 0, NULL, }, }; -START_TEST(test_exec_fetch) +START_TEST(test_query) { sdb_conn_t *conn = mock_conn_create(); - sdb_store_matcher_t *filter = NULL; uint32_t code = UINT32_MAX, msg_len = UINT32_MAX; const char *data; @@ -342,62 +538,51 @@ START_TEST(test_exec_fetch) size_t len; int check; - if (exec_fetch_data[_i].filter) { - filter = sdb_fe_parse_matcher(exec_fetch_data[_i].filter, -1, NULL); - ck_assert_msg(filter != NULL); - } + conn->cmd = SDB_CONNECTION_QUERY; + conn->cmd_len = (uint32_t)strlen(query_data[_i].query); + sdb_strbuf_memcpy(conn->buf, query_data[_i].query, conn->cmd_len); - check = sdb_fe_exec_fetch(conn, exec_fetch_data[_i].type, - exec_fetch_data[_i].hostname, exec_fetch_data[_i].name, filter); - fail_unless(check == exec_fetch_data[_i].expected, - "sdb_fe_exec_fetch(%s, %s, %s, %s) = %d; expected: %d", - SDB_STORE_TYPE_TO_NAME(exec_fetch_data[_i].type), - exec_fetch_data[_i].hostname, exec_fetch_data[_i].name, - exec_fetch_data[_i].filter, check, exec_fetch_data[_i].expected); + check = sdb_fe_query(conn); + fail_unless(check == query_data[_i].expected, + "sdb_fe_query(%s) = %d; expected: %d", + query_data[_i].query, check, query_data[_i].expected); + data = sdb_strbuf_string(MOCK_CONN(conn)->write_buf); len = sdb_strbuf_len(MOCK_CONN(conn)->write_buf); - if (exec_fetch_data[_i].code == UINT32_MAX) { + if (query_data[_i].code == UINT32_MAX) { fail_unless(len == 0, - "sdb_fe_exec_fetch(%s, %s, %s, %s) returned data on error", - SDB_STORE_TYPE_TO_NAME(exec_fetch_data[_i].type), - exec_fetch_data[_i].hostname, exec_fetch_data[_i].name, - exec_fetch_data[_i].filter); - sdb_object_deref(SDB_OBJ(filter)); + "sdb_fe_query(%s) returned data on error: '%s'", + query_data[_i].query, data); mock_conn_destroy(conn); return; } - data = sdb_strbuf_string(MOCK_CONN(conn)->write_buf); tmp = sdb_proto_unmarshal_header(data, len, &code, &msg_len); ck_assert_msg(tmp == (ssize_t)(2 * sizeof(uint32_t))); data += tmp; len -= tmp; - fail_unless((code == exec_fetch_data[_i].code) - && ((size_t)msg_len == exec_fetch_data[_i].len), - "sdb_fe_exec_fetch(%s, %s, %s, %s) returned %u, %u; expected: %u, %zu", - SDB_STORE_TYPE_TO_NAME(exec_fetch_data[_i].type), - exec_fetch_data[_i].hostname, exec_fetch_data[_i].name, - exec_fetch_data[_i].filter, code, msg_len, - exec_fetch_data[_i].code, exec_fetch_data[_i].len); - - tmp = sdb_proto_unmarshal_int32(data, len, &code); - fail_unless(code == SDB_CONNECTION_FETCH, - "sdb_fe_exec_fetch(%s, %s, %s, %s) returned %s object; expected: FETCH", - SDB_STORE_TYPE_TO_NAME(exec_fetch_data[_i].type), - exec_fetch_data[_i].hostname, exec_fetch_data[_i].name, - exec_fetch_data[_i].filter, SDB_CONN_MSGTYPE_TO_STRING((int)code)); - data += tmp; - len -= tmp; + fail_unless((code == query_data[_i].code) + && ((size_t)msg_len == query_data[_i].len), + "sdb_fe_query(%s) returned %u, %u; expected: %u, %zu", + query_data[_i].query, code, msg_len, + query_data[_i].code, query_data[_i].len); + + if (code == SDB_CONNECTION_DATA) { + tmp = sdb_proto_unmarshal_int32(data, len, &code); + fail_unless(code == query_data[_i].type, + "sdb_fe_query(%s) returned %s object; expected: %s", + query_data[_i].query, SDB_CONN_MSGTYPE_TO_STRING((int)code), + SDB_CONN_MSGTYPE_TO_STRING((int)query_data[_i].type)); + data += tmp; + len -= tmp; + } - fail_if_strneq(data, exec_fetch_data[_i].data, (size_t)msg_len, - "sdb_fe_exec_fetch(%s, %s, %s, %s) returned '%s'; expected: '%s'", - SDB_STORE_TYPE_TO_NAME(exec_fetch_data[_i].type), - exec_fetch_data[_i].hostname, exec_fetch_data[_i].name, - exec_fetch_data[_i].filter, data, exec_fetch_data[_i].data); + fail_if_strneq(data, query_data[_i].data, (size_t)msg_len, + "sdb_fe_query(%s) returned unexpected data", + query_data[_i].query, data, query_data[_i].data); - sdb_object_deref(SDB_OBJ(filter)); mock_conn_destroy(conn); } END_TEST @@ -406,7 +591,7 @@ TEST_MAIN("frontend::query") { TCase *tc = tcase_create("core"); tcase_add_checked_fixture(tc, populate, sdb_store_clear); - tcase_add_loop_test(tc, test_exec_fetch, 0, SDB_STATIC_ARRAY_LEN(exec_fetch_data)); + TC_ADD_LOOP_TEST(tc, query); ADD_TCASE(tc); } TEST_MAIN_END diff --git a/t/unit/parser/parser_test.c b/t/unit/parser/parser_test.c index cfc36fc..f363dc2 100644 --- a/t/unit/parser/parser_test.c +++ b/t/unit/parser/parser_test.c @@ -31,6 +31,7 @@ #include "parser/parser.h" #include "core/object.h" +#include "core/store.h" #include "testutils.h" #include @@ -634,6 +635,7 @@ START_TEST(test_parse) sdb_strbuf_t *errbuf = sdb_strbuf_create(64); sdb_llist_t *check; sdb_ast_node_t *node; + sdb_store_matcher_t *m; _Bool ok; check = sdb_parser_parse(parse_data[_i].query, @@ -695,7 +697,14 @@ START_TEST(test_parse) SDB_STORE_TYPE_TO_NAME(parse_data[_i].expected_extra)); } + /* TODO: this should move into front-end specific tests */ + m = sdb_store_query_prepare(node); + fail_unless(m != NULL, + "sdb_store_query_prepare(AST<%s>) = NULL; expected: ", + parse_data[_i].query); + sdb_object_deref(SDB_OBJ(node)); + sdb_object_deref(SDB_OBJ(m)); sdb_llist_destroy(check); sdb_strbuf_destroy(errbuf); }