diff --git a/src/parser/analyzer.c b/src/parser/analyzer.c
index 867af5211dbbbc5f36174236584c06fbb00e571a..f352a0d630cf2ae5d7e69534658e4493724a1e3e 100644 (file)
--- a/src/parser/analyzer.c
+++ b/src/parser/analyzer.c
#include "utils/strbuf.h"
#include <assert.h>
+#include <stdarg.h>
#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);
+/*
+ * error reporting
+ */
+
+static void
+op_error(sdb_strbuf_t *errbuf, sdb_ast_op_t *op, const char *reason)
+{
+ sdb_strbuf_sprintf(errbuf, "Invalid operation %s %s %s (%s)",
+ SDB_TYPE_TO_STRING(op->left->data_type),
+ SDB_AST_OP_TO_STRING(op->kind),
+ SDB_TYPE_TO_STRING(op->right->data_type),
+ reason);
+} /* op_error */
+
+static void
+__attribute__((format(printf, 3, 4)))
+iter_error(sdb_strbuf_t *errbuf, sdb_ast_iter_t *iter, const char *reason, ...)
+{
+ char r[1024];
+ va_list ap;
+
+ va_start(ap, reason);
+ vsnprintf(r, sizeof(r), reason, ap);
+ va_end(ap);
+
+ assert((iter->expr->type == SDB_AST_TYPE_OPERATOR)
+ && (! SDB_AST_OP(iter->expr)->left));
+ sdb_strbuf_sprintf(errbuf, "Invalid iterator %s %s %s %s (%s)",
+ SDB_AST_OP_TO_STRING(iter->kind),
+ SDB_TYPE_TO_STRING(iter->iter->data_type),
+ SDB_AST_OP_TO_STRING(SDB_AST_OP(iter->expr)->kind),
+ SDB_TYPE_TO_STRING(SDB_AST_OP(iter->expr)->right->data_type),
+ r);
+} /* iter_error */
+
/*
* expression nodes
*/
switch (op->kind) {
case SDB_AST_OR:
case SDB_AST_AND:
+ if (! SDB_AST_IS_LOGICAL(op->left)) {
+ sdb_strbuf_sprintf(errbuf, "Invalid left operand (%s) "
+ "in %s expression", SDB_AST_TYPE_TO_STRING(op->left),
+ SDB_AST_OP_TO_STRING(op->kind));
+ return -1;
+ }
if (analyze_node(context, op->left, errbuf))
return -1;
/* fallthrough */
case SDB_AST_NOT:
+ if (! SDB_AST_IS_LOGICAL(op->right)) {
+ sdb_strbuf_sprintf(errbuf, "Invalid right operand (%s) "
+ "in %s expression", SDB_AST_TYPE_TO_STRING(op->right),
+ SDB_AST_OP_TO_STRING(op->kind));
+ return -1;
+ }
if (analyze_node(context, op->right, errbuf))
return -1;
break;
return -1;
if (analyze_node(context, op->right, errbuf))
return -1;
+
+ if ((op->left->data_type > 0) && (op->right->data_type > 0)) {
+ if (op->left->data_type == op->right->data_type)
+ return 0;
+ op_error(errbuf, op, "type mismatch");
+ return -1;
+ }
+ if ((op->left->data_type > 0) && (op->left->data_type & SDB_TYPE_ARRAY)) {
+ op_error(errbuf, op, "array not allowed");
+ return -1;
+ }
+ if ((op->right->data_type > 0) && (op->right->data_type & SDB_TYPE_ARRAY)) {
+ op_error(errbuf, op, "array not allowed");
+ return -1;
+ }
break;
}
return -1;
if (analyze_node(context, op->right, errbuf))
return -1;
+
+ /* all types are supported for the left operand
+ * TODO: introduce a cast operator if it's not a string */
+ if ((op->right->data_type > 0)
+ && (op->right->data_type != SDB_TYPE_REGEX)
+ && (op->right->data_type != SDB_TYPE_STRING)) {
+ op_error(errbuf, op, "invalid regex");
+ return -1;
+ }
break;
case SDB_AST_ISNULL:
+ case SDB_AST_ISTRUE:
+ case SDB_AST_ISFALSE:
if (analyze_node(context, op->right, errbuf))
return -1;
break;
return -1;
if (analyze_node(context, op->right, errbuf))
return -1;
+
+ if ((op->right->data_type > 0) && (! (op->right->data_type & SDB_TYPE_ARRAY))) {
+ op_error(errbuf, op, "array expected");
+ return -1;
+ }
+ /* the left operand may be a scalar or an array but the element
+ * type has to match */
+ if ((op->left->data_type > 0) && (op->right->data_type > 0)
+ && ((op->left->data_type & 0xff) != (op->right->data_type & 0xff))) {
+ op_error(errbuf, op, "type mismatch");
+ return -1;
+ }
break;
default:
- sdb_strbuf_sprintf(errbuf, "Unknown matcher type %d", op->kind);
+ sdb_strbuf_sprintf(errbuf, "Unknown operand type %d", op->kind);
return -1;
}
return 0;
SDB_AST_NODE(op)->data_type = sdb_data_expr_type(SDB_AST_OP_TO_DATA_OP(op->kind),
op->left->data_type, op->right->data_type);
+ if ((op->left->data_type > 0) && (op->right->data_type > 0)
+ && (SDB_AST_NODE(op)->data_type <= 0)) {
+ op_error(errbuf, op, "type mismatch");
+ return -1;
+ }
+
/* TODO: replace constant arithmetic operations with a constant value */
return 0;
} /* analyze_arith */
analyze_iter(int context, sdb_ast_iter_t *iter, sdb_strbuf_t *errbuf)
{
sdb_ast_const_t c = SDB_AST_CONST_INIT;
+ int iter_context = context;
int status;
- if (analyze_node(context, iter->iter, errbuf))
- return -1;
- c.super.data_type = iter->iter->data_type;
+ if (iter->iter->type == SDB_AST_TYPE_TYPED)
+ iter_context = SDB_AST_TYPED(iter->iter)->type;
+ if (analyze_node(iter_context, iter->iter, errbuf))
+ return -1;
/* TODO: support other setups as well */
assert((iter->expr->type == SDB_AST_TYPE_OPERATOR)
&& (! SDB_AST_OP(iter->expr)->left));
+ /* 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)) {
+ iter_error(errbuf, iter, "%s not iterable in %s context",
+ SDB_STORE_TYPE_TO_NAME(iter_type),
+ SDB_STORE_TYPE_TO_NAME(context));
+ return -1;
+ }
+
+ if ((context == iter_type)
+ || ((iter_type != SDB_SERVICE)
+ && (iter_type != SDB_METRIC)
+ && (iter_type != SDB_ATTRIBUTE))
+ || ((context == SDB_SERVICE)
+ && (iter_type == SDB_METRIC))
+ || ((context == SDB_METRIC)
+ && (iter_type == SDB_SERVICE))) {
+ iter_error(errbuf, iter, "%s not iterable in %s context",
+ SDB_STORE_TYPE_TO_NAME(iter_type),
+ SDB_STORE_TYPE_TO_NAME(context));
+ return -1;
+ }
+ }
+ else if (iter->iter->type == SDB_AST_TYPE_VALUE) {
+ int iter_type = SDB_AST_VALUE(iter->iter)->type;
+
+ 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"
+ : SDB_FIELD_TO_NAME(iter_type),
+ SDB_STORE_TYPE_TO_NAME(context));
+ return -1;
+ }
+ }
+ 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;
} /* 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(v->type));
+ 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 */
}
if (fetch->filter)
- return analyze_node(-1, fetch->filter, errbuf);
+ return analyze_node(FILTER_CONTEXT, fetch->filter, errbuf);
return 0;
} /* analyze_fetch */
return -1;
}
if (list->filter)
- return analyze_node(-1, list->filter, errbuf);
+ return analyze_node(FILTER_CONTEXT, list->filter, errbuf);
return 0;
} /* analyze_list */
if (analyze_node(lookup->obj_type, lookup->matcher, errbuf))
return -1;
if (lookup->filter)
- return analyze_node(-1, lookup->filter, errbuf);
+ return analyze_node(FILTER_CONTEXT, lookup->filter, errbuf);
return 0;
} /* analyze_lookup */
SDB_AST_TYPE_TO_STRING(node));
return -1;
}
- return analyze_node(-1, node, errbuf);
+ return analyze_node(UNSPEC_CONTEXT, node, errbuf);
} /* sdb_parser_analyze_conditional */
int
SDB_AST_TYPE_TO_STRING(node));
return -1;
}
- return analyze_node(-1, node, errbuf);
+ return analyze_node(UNSPEC_CONTEXT, node, errbuf);
} /* sdb_parser_analyze_arith */
/* vim: set tw=78 sw=4 ts=4 noexpandtab : */