Code

parser: Require a context for each parser operation.
[sysdb.git] / src / parser / analyzer.c
index 2f79fc394c79dcf2f2fb65087942935db5efd59e..5b89016609e0aaa8aba20a6a4a9f5d648262fb80 100644 (file)
@@ -38,7 +38,6 @@
 #define VALID_OBJ_TYPE(t) ((SDB_HOST <= (t)) && ((t) <= SDB_METRIC))
 
 #define FILTER_CONTEXT -1
-#define UNSPEC_CONTEXT -2
 
 static int
 analyze_node(int context, sdb_ast_node_t *node, sdb_strbuf_t *errbuf);
@@ -155,6 +154,8 @@ analyze_logical(int context, sdb_ast_op_t *op, sdb_strbuf_t *errbuf)
                break;
 
        case SDB_AST_ISNULL:
+       case SDB_AST_ISTRUE:
+       case SDB_AST_ISFALSE:
                if (analyze_node(context, op->right, errbuf))
                        return -1;
                break;
@@ -215,30 +216,24 @@ analyze_iter(int context, sdb_ast_iter_t *iter, sdb_strbuf_t *errbuf)
        if (iter->iter->type == SDB_AST_TYPE_TYPED)
                iter_context = SDB_AST_TYPED(iter->iter)->type;
 
-       if (analyze_node(context, iter->iter, errbuf))
+       if (analyze_node(iter_context, iter->iter, errbuf))
                return -1;
-       if (iter->iter->data_type > 0)
-               c.value.type = iter->iter->data_type & 0xff;
-       else
-               c.value.type = -1;
-
        /* TODO: support other setups as well */
        assert((iter->expr->type == SDB_AST_TYPE_OPERATOR)
                        && (! SDB_AST_OP(iter->expr)->left));
-       SDB_AST_OP(iter->expr)->left = SDB_AST_NODE(&c);
-       status = analyze_node(iter_context, iter->expr, errbuf);
-       SDB_AST_OP(iter->expr)->left = NULL;
-       if (status)
-               return -1;
+       /* determine the data-type for better error messages */
+       analyze_node(iter_context, SDB_AST_OP(iter->expr)->right, NULL);
 
        if (iter->iter->type == SDB_AST_TYPE_TYPED) {
                int iter_type = SDB_AST_TYPED(iter->iter)->type;
 
+               c.value.type = iter->iter->data_type;
+
                if (iter_type == SDB_ATTRIBUTE) {
                        /* attributes are always iterable */
                }
                else if ((context != SDB_HOST) && (context != SDB_SERVICE)
-                               && (context != SDB_METRIC) && (context != UNSPEC_CONTEXT)) {
+                               && (context != SDB_METRIC)) {
                        iter_error(errbuf, iter, "%s not iterable in %s context",
                                        SDB_STORE_TYPE_TO_NAME(iter_type),
                                        SDB_STORE_TYPE_TO_NAME(context));
@@ -262,12 +257,9 @@ analyze_iter(int context, sdb_ast_iter_t *iter, sdb_strbuf_t *errbuf)
        else if (iter->iter->type == SDB_AST_TYPE_VALUE) {
                int iter_type = SDB_AST_VALUE(iter->iter)->type;
 
-               if (iter_type == SDB_FIELD_BACKEND) {
-                       /* backends are always iterable */
-               }
-               else if ((context != SDB_HOST) && (context != SDB_SERVICE)
-                               && (context != SDB_METRIC) && (context != SDB_ATTRIBUTE)
-                               && (context != UNSPEC_CONTEXT)) {
+               c.value.type = iter->iter->data_type & 0xff;
+
+               if (iter_type != SDB_FIELD_BACKEND) {
                        iter_error(errbuf, iter, "%s not iterable in %s context",
                                        (iter_type == SDB_ATTRIBUTE)
                                                ? "attribute"
@@ -277,12 +269,27 @@ analyze_iter(int context, sdb_ast_iter_t *iter, sdb_strbuf_t *errbuf)
                }
        }
        else if (iter->iter->type == SDB_AST_TYPE_CONST) {
+               c.value.type = iter->iter->data_type & 0xff;
+
                if (! (SDB_AST_CONST(iter->iter)->value.type & SDB_TYPE_ARRAY)) {
                        iter_error(errbuf, iter, "%s not iterable",
                                        SDB_TYPE_TO_STRING(SDB_AST_CONST(iter->iter)->value.type));
                        return -1;
                }
        }
+       else {
+               /* TODO: if we know the data-type of iter->iter and it's an array,
+                * we should support an iterator for it as well */
+               iter_error(errbuf, iter, "%s expression not iterable",
+                               SDB_AST_TYPE_TO_STRING(iter->iter));
+               return -1;
+       }
+
+       SDB_AST_OP(iter->expr)->left = SDB_AST_NODE(&c);
+       status = analyze_node(context, iter->expr, errbuf);
+       SDB_AST_OP(iter->expr)->left = NULL;
+       if (status)
+               return -1;
        return 0;
 } /* analyze_iter */
 
@@ -295,21 +302,67 @@ analyze_const(int __attribute__((unused)) context, sdb_ast_const_t *c,
 } /* analyze_const */
 
 static int
-analyze_value(int __attribute__((unused)) context, sdb_ast_value_t *v,
-               sdb_strbuf_t __attribute__((unused)) *errbuf)
+analyze_value(int context, sdb_ast_value_t *v, sdb_strbuf_t *errbuf)
 {
        if (v->type != SDB_ATTRIBUTE)
                SDB_AST_NODE(v)->data_type = SDB_FIELD_TYPE(v->type);
+
+       if ((v->type != SDB_ATTRIBUTE) && v->name) {
+               sdb_strbuf_sprintf(errbuf, "Invalid expression %s[%s]",
+                               SDB_FIELD_TO_NAME(v->type), v->name);
+               return -1;
+       }
+       else if ((v->type == SDB_ATTRIBUTE) && (! v->name)) {
+               sdb_strbuf_sprintf(errbuf, "Invalid expression attribute[] "
+                               "(missing name)");
+               return -1;
+       }
+
+       if ((context != SDB_ATTRIBUTE) && (v->type == SDB_FIELD_VALUE)) {
+               sdb_strbuf_sprintf(errbuf, "Invalid expression %s.value",
+                               SDB_FIELD_TO_NAME(context));
+               return -1;
+       }
+       if ((context != SDB_METRIC) && (v->type == SDB_FIELD_TIMESERIES)) {
+               sdb_strbuf_sprintf(errbuf, "Invalid expression %s.timeseries",
+                               SDB_FIELD_TO_NAME(context));
+               return -1;
+       }
        return 0;
 } /* analyze_value */
 
 static int
-analyze_typed(int __attribute__((unused)) context, sdb_ast_typed_t *t,
-               sdb_strbuf_t *errbuf)
+analyze_typed(int context, sdb_ast_typed_t *t, sdb_strbuf_t *errbuf)
 {
+       if ((t->expr->type != SDB_AST_TYPE_VALUE)
+                       && (t->expr->type != SDB_AST_TYPE_TYPED)) {
+               sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s",
+                               SDB_STORE_TYPE_TO_NAME(t->type),
+                               SDB_AST_TYPE_TO_STRING(t->expr));
+               return -1;
+       }
        if (analyze_node(t->type, t->expr, errbuf))
                return -1;
        SDB_AST_NODE(t)->data_type = t->expr->data_type;
+
+       if ((t->type != SDB_ATTRIBUTE) && (! VALID_OBJ_TYPE(t->type))) {
+               sdb_strbuf_sprintf(errbuf, "Invalid expression %#x.%s",
+                               t->type, SDB_AST_TYPE_TO_STRING(t->expr));
+               return -1;
+       }
+
+       /* self-references are allowed and services and metrics may reference
+        * their parent host; everything may reference attributes */
+       if ((context != t->type) && (context > 0)
+                       && (((context != SDB_SERVICE) && (context != SDB_METRIC))
+                               || (t->type != SDB_HOST))
+                       && (t->type != SDB_ATTRIBUTE)) {
+               sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s in %s context",
+                               SDB_STORE_TYPE_TO_NAME(t->type),
+                               SDB_AST_TYPE_TO_STRING(t->expr),
+                               context == -1 ? "generic" : SDB_STORE_TYPE_TO_NAME(context));
+               return -1;
+       }
        return 0;
 } /* analyze_typed */
 
@@ -509,8 +562,8 @@ analyze_timeseries(sdb_ast_timeseries_t *ts, sdb_strbuf_t *errbuf)
        }
        if (ts->end <= ts->start) {
                char start_str[64], end_str[64];
-               sdb_strftime(start_str, sizeof(start_str), "%F %T Tz", ts->start);
-               sdb_strftime(end_str, sizeof(end_str), "%F %T Tz", ts->end);
+               sdb_strftime(start_str, sizeof(start_str), ts->start);
+               sdb_strftime(end_str, sizeof(end_str), ts->end);
                sdb_strbuf_sprintf(errbuf, "Start time (%s) greater than "
                                "end time (%s) in STORE command", start_str, end_str);
                return -1;
@@ -550,8 +603,13 @@ sdb_parser_analyze(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
 } /* sdb_parser_analyze */
 
 int
-sdb_parser_analyze_conditional(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
+sdb_parser_analyze_conditional(int context,
+               sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
 {
+       if (! VALID_OBJ_TYPE(context)) {
+               sdb_strbuf_sprintf(errbuf, "Invalid object type %#x", context);
+               return -1;
+       }
        if (! node) {
                sdb_strbuf_sprintf(errbuf, "Empty conditional node");
                return -1;
@@ -561,12 +619,17 @@ sdb_parser_analyze_conditional(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
                                SDB_AST_TYPE_TO_STRING(node));
                return -1;
        }
-       return analyze_node(UNSPEC_CONTEXT, node, errbuf);
+       return analyze_node(context, node, errbuf);
 } /* sdb_parser_analyze_conditional */
 
 int
-sdb_parser_analyze_arith(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
+sdb_parser_analyze_arith(int context,
+               sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
 {
+       if (! VALID_OBJ_TYPE(context)) {
+               sdb_strbuf_sprintf(errbuf, "Invalid object type %#x", context);
+               return -1;
+       }
        if (! node) {
                sdb_strbuf_sprintf(errbuf, "Empty arithmetic node");
                return -1;
@@ -576,7 +639,7 @@ sdb_parser_analyze_arith(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
                                SDB_AST_TYPE_TO_STRING(node));
                return -1;
        }
-       return analyze_node(UNSPEC_CONTEXT, node, errbuf);
+       return analyze_node(context, node, errbuf);
 } /* sdb_parser_analyze_arith */
 
 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */