Code

Merged branch 'master' of git://git.tokkee.org/sysdb.
authorSebastian Harl <sh@tokkee.org>
Fri, 15 May 2015 20:53:44 +0000 (22:53 +0200)
committerSebastian Harl <sh@tokkee.org>
Fri, 15 May 2015 20:53:44 +0000 (22:53 +0200)
12 files changed:
src/Makefile.am
src/core/store-private.h
src/core/store.c
src/core/store_lookup.c
src/core/store_query.c [new file with mode: 0644]
src/frontend/analyzer.c
src/frontend/store.c
src/include/core/store.h
src/parser/analyzer.c
t/coverage.sh
t/unit/frontend/query_test.c
t/unit/parser/parser_test.c

index ed81316a6e727dab7a482aca0f7fee46f4ba6561..6b560df7b8b2bc6c56d6615c7b7e516dc8129fd2 100644 (file)
@@ -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 \
index 874dc0577a9fe030e64fa7d59b1cfb2c4ac5d709..060ec7bccf02c7b19acfccc7a6d29b5058986c01 100644 (file)
@@ -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
index ea30da90116d9925cd148e39f7577e346503ba42..eb58ad83b1ba2cd36b5fba803e42313758ea3d38 100644 (file)
@@ -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
index 18a0235536e3f154128ff576cbc624c9dfa49a5f..4862905b3366d4fa4455a95ddc80a5bedd8ee652 100644 (file)
@@ -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 (file)
index 0000000..90d45bf
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * SysDB - src/core/store_lookup.c
+ * Copyright (C) 2014-2015 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * 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 <assert.h>
+
+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 : */
index 4bd370222e8e11d4a41e9a3b297a1e35412155be..01e1ad357adec6502fc7efc74768bee7cdf03969 100644 (file)
@@ -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",
index ff563165cb1bf7ebda020c1ac2f630e13990e571..6cad8cd312637036af82ef0a763eba7fbfd46c20 100644 (file)
@@ -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));
index 94a571345852481b89479fd6664f2fc484c103b9..0abf0cb467d3d0a66fb3f99008537bce2f8848de 100644 (file)
@@ -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 <stdbool.h>
@@ -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
index 01539edbe71f870de8486b52320caa6dd9fa4444..e03cbdbc3d2e9ff259e9d18f6729bf9751e920b7 100644 (file)
@@ -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)) {
index d6f00bebfb6458180aab5e6b28f2978f3dd7166d..d896156bf1f4bddbd14a23ef6c58f560d655c977 100755 (executable)
@@ -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 \
index 73f3042ee9516a8605346393ab4ca456cdb2dd5c..7e15502096a04ed83358c1fb41f5aab8e14c098a 100644 (file)
@@ -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
index cfc36fc36417610175455f3e7bef2a8cc844b7c6..f363dc2b6d3e9f51ca87780026b8ec051bd918e7 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "parser/parser.h"
 #include "core/object.h"
+#include "core/store.h"
 #include "testutils.h"
 
 #include <check.h>
@@ -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: <m>",
+                       parse_data[_i].query);
+
        sdb_object_deref(SDB_OBJ(node));
+       sdb_object_deref(SDB_OBJ(m));
        sdb_llist_destroy(check);
        sdb_strbuf_destroy(errbuf);
 }