From 1ea57919cda96d83e7625999019269dffefdb329 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Wed, 15 Apr 2015 11:38:26 +0200 Subject: [PATCH] Migrate top-level checks to the new parser/analyzer. Call the analyzer from sdb_parser_parse(). --- src/Makefile.am | 1 + src/include/parser/parser.h | 12 ++ src/parser/analyzer.c | 257 ++++++++++++++++++++++++++++++++++++ src/parser/parser.c | 9 +- 4 files changed, 273 insertions(+), 6 deletions(-) create mode 100644 src/parser/analyzer.c diff --git a/src/Makefile.am b/src/Makefile.am index 5a3146a..ed81316 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -92,6 +92,7 @@ libsysdb_la_SOURCES = \ frontend/session.c \ frontend/store.c \ frontend/query.c \ + parser/analyzer.c \ parser/ast.c include/parser/ast.h \ parser/parser.c include/parser/parser.h \ utils/avltree.c include/utils/avltree.h \ diff --git a/src/include/parser/parser.h b/src/include/parser/parser.h index 9235ef5..0a7c720 100644 --- a/src/include/parser/parser.h +++ b/src/include/parser/parser.h @@ -72,6 +72,18 @@ sdb_parser_parse_conditional(const char *cond, int len, sdb_strbuf_t *errbuf); sdb_ast_node_t * sdb_parser_parse_arith(const char *expr, int len, sdb_strbuf_t *errbuf); +/* + * sdb_parser_analyze: + * Semantical analysis of a parse-tree. + * + * Returns: + * - 0 on success + * - a negative value else; an error message will be written to the provided + * error buffer + */ +int +sdb_parser_analyze(sdb_ast_node_t *node, sdb_strbuf_t *errbuf); + /* * Low-level interface. */ diff --git a/src/parser/analyzer.c b/src/parser/analyzer.c new file mode 100644 index 0000000..244966b --- /dev/null +++ b/src/parser/analyzer.c @@ -0,0 +1,257 @@ +/* + * SysDB - src/parser/analyzer.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 "sysdb.h" + +#include "parser/ast.h" +#include "parser/parser.h" +#include "utils/error.h" +#include "utils/strbuf.h" + +#include + +#define VALID_OBJ_TYPE(t) ((SDB_HOST <= (t)) && ((t) <= SDB_METRIC)) + +/* + * private helper functions + */ + +static int +analyze_node(int context, sdb_ast_node_t *node, sdb_strbuf_t *errbuf) +{ + (void)context; + if (! node) { + sdb_strbuf_sprintf(errbuf, "Empty AST node"); + return -1; + } + return 0; +} /* analyze_node */ + +/* + * top level / command nodes + */ + +static int +analyze_fetch(sdb_ast_fetch_t *fetch, sdb_strbuf_t *errbuf) +{ + if (! VALID_OBJ_TYPE(fetch->obj_type)) { + sdb_strbuf_sprintf(errbuf, "Invalid object type %#x " + "in FETCH command", fetch->obj_type); + return -1; + } + if (! fetch->name) { + sdb_strbuf_sprintf(errbuf, "Missing object name in " + "FETCH %s command", SDB_STORE_TYPE_TO_NAME(fetch->obj_type)); + return -1; + } + + if ((fetch->obj_type == SDB_HOST) && fetch->hostname) { + sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' " + "in FETCH HOST command", fetch->hostname); + return -1; + } + else if ((fetch->obj_type != SDB_HOST) && (! fetch->hostname)) { + sdb_strbuf_sprintf(errbuf, "Missing parent hostname for '%s' " + "in FETCH %s command", fetch->name, + SDB_STORE_TYPE_TO_NAME(fetch->obj_type)); + return -1; + } + + if (fetch->filter) + return analyze_node(-1, fetch->filter, errbuf); + return 0; +} + +static int +analyze_list(sdb_ast_list_t *list, sdb_strbuf_t *errbuf) +{ + if (! VALID_OBJ_TYPE(list->obj_type)) { + sdb_strbuf_sprintf(errbuf, "Invalid object type %#x " + "in LIST command", list->obj_type); + return -1; + } + if (list->filter) + return analyze_node(-1, list->filter, errbuf); + return 0; +} + +static int +analyze_lookup(sdb_ast_lookup_t *lookup, sdb_strbuf_t *errbuf) +{ + if (! VALID_OBJ_TYPE(lookup->obj_type)) { + sdb_strbuf_sprintf(errbuf, "Invalid object type %#x " + "in LOOKUP command", lookup->obj_type); + return -1; + } + if (lookup->matcher) + if (analyze_node(lookup->obj_type, lookup->matcher, errbuf)) + return -1; + if (lookup->filter) + return analyze_node(-1, lookup->filter, errbuf); + return 0; +} + +static int +analyze_store(sdb_ast_store_t *st, sdb_strbuf_t *errbuf) +{ + if ((st->obj_type != SDB_ATTRIBUTE) + && (! VALID_OBJ_TYPE(st->obj_type))) { + sdb_strbuf_sprintf(errbuf, "Invalid object type %#x " + "in STORE command", st->obj_type); + return -1; + } + if (! st->name) { + sdb_strbuf_sprintf(errbuf, "Missing object name in " + "STORE %s command", SDB_STORE_TYPE_TO_NAME(st->obj_type)); + return -1; + } + + if ((st->obj_type == SDB_HOST) && st->hostname) { + sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' " + "in STORE HOST command", st->hostname); + return -1; + } + else if ((st->obj_type != SDB_HOST) && (! st->hostname)) { + sdb_strbuf_sprintf(errbuf, "Missing parent hostname for '%s' " + "in STORE %s command", st->name, + SDB_STORE_TYPE_TO_NAME(st->obj_type)); + return -1; + } + + if (st->obj_type == SDB_ATTRIBUTE) { + if ((st->parent_type <= 0) && st->parent) { + sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' " + "in STORE %s command", st->parent, + SDB_STORE_TYPE_TO_NAME(st->obj_type)); + return -1; + } + else if (st->parent_type > 0) { + if (! VALID_OBJ_TYPE(st->parent_type)) { + sdb_strbuf_sprintf(errbuf, "Invalid parent type %#x " + "in STORE %s command", st->parent_type, + SDB_STORE_TYPE_TO_NAME(st->obj_type)); + return -1; + } + if (! st->parent) { + sdb_strbuf_sprintf(errbuf, "Missing %s parent name " + "in STORE %s command", + SDB_STORE_TYPE_TO_NAME(st->parent_type), + SDB_STORE_TYPE_TO_NAME(st->obj_type)); + return -1; + } + } + } + else if ((st->parent_type > 0) || st->parent) { + sdb_strbuf_sprintf(errbuf, "Unexpected %s parent name '%s' " + "in STORE %s command", + SDB_STORE_TYPE_TO_NAME(st->parent_type), + st->parent ? st->parent : "", + SDB_STORE_TYPE_TO_NAME(st->obj_type)); + return -1; + } + + if (st->obj_type == SDB_METRIC) { + if ((! st->store_type) != (! st->store_id)) { + sdb_strbuf_sprintf(errbuf, "Incomplete metric store %s %s " + "in STORE METRIC command", + st->store_type ? st->store_type : "", + st->store_id ? st->store_id : ""); + return -1; + } + } + else if (st->store_type || st->store_id) { + sdb_strbuf_sprintf(errbuf, "Unexpected metric store %s %s " + "in STORE %s command", + st->store_type ? st->store_type : "", + st->store_id ? st->store_id : "", + SDB_STORE_TYPE_TO_NAME(st->obj_type)); + return -1; + } + + if ((! (st->obj_type == SDB_ATTRIBUTE)) + && (st->value.type != SDB_TYPE_NULL)) { + char v_str[sdb_data_format(&st->value, NULL, 0, SDB_DOUBLE_QUOTED) + 1]; + sdb_data_format(&st->value, v_str, sizeof(v_str), SDB_DOUBLE_QUOTED); + sdb_strbuf_sprintf(errbuf, "Unexpected value %s in STORE %s command", + v_str, SDB_STORE_TYPE_TO_NAME(st->obj_type)); + return -1; + } + return 0; +} + +static int +analyze_timeseries(sdb_ast_timeseries_t *ts, sdb_strbuf_t *errbuf) +{ + if (! ts->hostname) { + sdb_strbuf_sprintf(errbuf, "Missing hostname in STORE command"); + return -1; + } + if (! ts->metric) { + sdb_strbuf_sprintf(errbuf, "Missing metric name in STORE command"); + return -1; + } + 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_strbuf_sprintf(errbuf, "Start time (%s) greater than " + "end time (%s) in STORE command", start_str, end_str); + return -1; + } + return 0; +} + +/* + * public API + */ + +int +sdb_parser_analyze(sdb_ast_node_t *node, sdb_strbuf_t *errbuf) +{ + if (! node) { + sdb_strbuf_sprintf(errbuf, "Empty AST node"); + return -1; + } + + if (node->type == SDB_AST_TYPE_FETCH) + return analyze_fetch(SDB_AST_FETCH(node), errbuf); + else if (node->type == SDB_AST_TYPE_LIST) + return analyze_list(SDB_AST_LIST(node), errbuf); + else if (node->type == SDB_AST_TYPE_LOOKUP) + return analyze_lookup(SDB_AST_LOOKUP(node), errbuf); + else if (node->type == SDB_AST_TYPE_STORE) + return analyze_store(SDB_AST_STORE(node), errbuf); + else if (node->type == SDB_AST_TYPE_TIMESERIES) + return analyze_timeseries(SDB_AST_TIMESERIES(node), errbuf); + + sdb_strbuf_sprintf(errbuf, "Invalid top-level AST node " + "of type %#x", node->type); + return -1; +} /* sdb_fe_analyze */ + +/* vim: set tw=78 sw=4 ts=4 noexpandtab : */ + diff --git a/src/parser/parser.c b/src/parser/parser.c index d22ce5f..de6085f 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -96,16 +96,13 @@ sdb_parser_parse(const char *query, int len, sdb_strbuf_t *errbuf) iter = sdb_llist_get_iter(yyextra.parsetree); while (sdb_llist_iter_has_next(iter)) { - sdb_conn_node_t *node; - node = SDB_CONN_NODE(sdb_llist_iter_get_next(iter)); - assert(node); - /* TODO - if (sdb_parser_analyze(node, errbuf)) { + sdb_ast_node_t *node; + node = SDB_AST_NODE(sdb_llist_iter_get_next(iter)); + if (sdb_parser_analyze(node, errbuf) < 0) { sdb_llist_iter_destroy(iter); sdb_llist_destroy(yyextra.parsetree); return NULL; } - */ } sdb_llist_iter_destroy(iter); return yyextra.parsetree; -- 2.30.2