X-Git-Url: https://git.tokkee.org/?p=sysdb.git;a=blobdiff_plain;f=src%2Fparser%2Fanalyzer.c;h=dea83d7e0c828d8966fbffe9e91a2895c466fdf7;hp=79c99e3e23e159ae8217a7dec323bf946c8d7e9b;hb=1ef0d00327e4b5f56b438f44053f48fd8b198baa;hpb=8092b5015036c07151a4da123b9db5b8b3f392ce diff --git a/src/parser/analyzer.c b/src/parser/analyzer.c index 79c99e3..dea83d7 100644 --- a/src/parser/analyzer.c +++ b/src/parser/analyzer.c @@ -33,12 +33,51 @@ #include "utils/strbuf.h" #include +#include #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 */ @@ -49,10 +88,22 @@ analyze_logical(int context, sdb_ast_op_t *op, sdb_strbuf_t *errbuf) 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; @@ -68,6 +119,21 @@ analyze_logical(int context, sdb_ast_op_t *op, sdb_strbuf_t *errbuf) 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; } @@ -77,9 +143,20 @@ analyze_logical(int context, sdb_ast_op_t *op, sdb_strbuf_t *errbuf) 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; @@ -89,10 +166,22 @@ analyze_logical(int context, sdb_ast_op_t *op, sdb_strbuf_t *errbuf) 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; @@ -105,9 +194,15 @@ analyze_arith(int context, sdb_ast_op_t *op, sdb_strbuf_t *errbuf) return -1; if (analyze_node(context, op->right, errbuf)) return -1; - SDB_AST_NODE(op)->data_type = sdb_data_expr_type(op->kind, + 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 */ @@ -116,15 +211,81 @@ static int 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; @@ -142,21 +303,72 @@ 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 != UNSPEC_CONTEXT) { + /* skip these checks if we don't know the context; it's up to the + * caller to check again once the right context information is + * available */ + 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 */ @@ -222,7 +434,7 @@ analyze_fetch(sdb_ast_fetch_t *fetch, sdb_strbuf_t *errbuf) } if (fetch->filter) - return analyze_node(-1, fetch->filter, errbuf); + return analyze_node(FILTER_CONTEXT, fetch->filter, errbuf); return 0; } /* analyze_fetch */ @@ -235,7 +447,7 @@ analyze_list(sdb_ast_list_t *list, sdb_strbuf_t *errbuf) return -1; } if (list->filter) - return analyze_node(-1, list->filter, errbuf); + return analyze_node(FILTER_CONTEXT, list->filter, errbuf); return 0; } /* analyze_list */ @@ -251,7 +463,7 @@ analyze_lookup(sdb_ast_lookup_t *lookup, sdb_strbuf_t *errbuf) 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 */ @@ -396,5 +608,35 @@ sdb_parser_analyze(sdb_ast_node_t *node, sdb_strbuf_t *errbuf) return -1; } /* sdb_parser_analyze */ +int +sdb_parser_analyze_conditional(sdb_ast_node_t *node, sdb_strbuf_t *errbuf) +{ + if (! node) { + sdb_strbuf_sprintf(errbuf, "Empty conditional node"); + return -1; + } + if (! SDB_AST_IS_LOGICAL(node)) { + sdb_strbuf_sprintf(errbuf, "Not a conditional node (got %s)", + SDB_AST_TYPE_TO_STRING(node)); + return -1; + } + return analyze_node(UNSPEC_CONTEXT, node, errbuf); +} /* sdb_parser_analyze_conditional */ + +int +sdb_parser_analyze_arith(sdb_ast_node_t *node, sdb_strbuf_t *errbuf) +{ + if (! node) { + sdb_strbuf_sprintf(errbuf, "Empty arithmetic node"); + return -1; + } + if (! SDB_AST_IS_ARITHMETIC(node)) { + sdb_strbuf_sprintf(errbuf, "Not an arithmetic node (got %s)", + SDB_AST_TYPE_TO_STRING(node)); + return -1; + } + return analyze_node(UNSPEC_CONTEXT, node, errbuf); +} /* sdb_parser_analyze_arith */ + /* vim: set tw=78 sw=4 ts=4 noexpandtab : */