From 69ba5856d2f9e396ce1956c24ed0194075800463 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Tue, 28 Oct 2014 00:08:07 +0100 Subject: [PATCH] frontend: Added basic support for semantic AST analysis. For now, the analyzer checks the types of all matcher operands. --- src/Makefile.am | 1 + src/frontend/analyzer.c | 176 ++++++++++++++++++++++++++++++++++ src/frontend/query.c | 33 ++++++- src/include/frontend/parser.h | 12 +++ 4 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 src/frontend/analyzer.c diff --git a/src/Makefile.am b/src/Makefile.am index a69769d..c8ad524 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -75,6 +75,7 @@ libsysdb_la_SOURCES = \ core/store_lookup.c \ core/data.c include/core/data.h \ core/timeseries.c include/core/timeseries.h \ + frontend/analyzer.c \ frontend/connection.c include/frontend/connection.h \ frontend/connection-private.h \ frontend/parser.c include/frontend/parser.h \ diff --git a/src/frontend/analyzer.c b/src/frontend/analyzer.c new file mode 100644 index 0000000..8c6b74d --- /dev/null +++ b/src/frontend/analyzer.c @@ -0,0 +1,176 @@ +/* + * SysDB - src/frontend/analyzer.c + * Copyright (C) 2014 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 +#include +#include + +#include + +/* + * private helper functions + */ + +static int +analyze_matcher(int context, sdb_store_matcher_t *m) +{ + int status = 0; + + if (! m) + return 0; + + switch (m->type) { + case MATCHER_OR: + case MATCHER_AND: + assert(OP_M(m)->left && OP_M(m)->right); + if (analyze_matcher(context, OP_M(m)->left)) + status = -1; + if (analyze_matcher(context, OP_M(m)->right)) + status = -1; + break; + + case MATCHER_NOT: + assert(UOP_M(m)->op); + if (analyze_matcher(context, UOP_M(m)->op)) + status = -1; + break; + + case MATCHER_ANY: + case MATCHER_ALL: + assert(ITER_M(m)->m); + if (ITER_M(m)->type == context) + status = -1; + if ((context != SDB_HOST) + && (context != SDB_SERVICE) + && (context != SDB_METRIC)) + status = -1; + if ((ITER_M(m)->type != SDB_SERVICE) + && (ITER_M(m)->type != SDB_METRIC) + && (ITER_M(m)->type != SDB_ATTRIBUTE)) + status = -1; + if ((context == SDB_SERVICE) + && (ITER_M(m)->type == SDB_METRIC)) + status = -1; + else if ((context == SDB_METRIC) + && (ITER_M(m)->type == SDB_SERVICE)) + status = -1; + if (analyze_matcher(ITER_M(m)->type, ITER_M(m)->m)) + status = -1; + break; + + case MATCHER_LT: + case MATCHER_LE: + case MATCHER_EQ: + case MATCHER_NE: + case MATCHER_GE: + case MATCHER_GT: + assert(CMP_M(m)->left && CMP_M(m)->right); + if ((CMP_M(m)->left->data_type > 0) + && (CMP_M(m)->left->data_type & SDB_TYPE_ARRAY)) + status = -1; + if ((CMP_M(m)->right->data_type > 0) + && (CMP_M(m)->right->data_type & SDB_TYPE_ARRAY)) + status = -1; + break; + + case MATCHER_IN: + if ((CMP_M(m)->left->data_type > 0) + && (CMP_M(m)->left->data_type & SDB_TYPE_ARRAY)) + status = -1; + if ((CMP_M(m)->right->data_type > 0) + && (! (CMP_M(m)->right->data_type & SDB_TYPE_ARRAY))) + status = -1; + break; + + case MATCHER_REGEX: + case MATCHER_NREGEX: + /* all types are supported for the left operand */ + if ((CMP_M(m)->right->data_type > 0) + && (CMP_M(m)->right->data_type != SDB_TYPE_REGEX) + && (CMP_M(m)->right->data_type != SDB_TYPE_STRING)) + status = -1; + break; + + case MATCHER_ISNULL: + case MATCHER_ISNNULL: + break; + + default: + return -1; + } + return status; +} /* analyze_matcher */ + +/* + * public API + */ + +int +sdb_fe_analyze(sdb_conn_node_t *node) +{ + sdb_store_matcher_t *m = NULL, *filter = NULL; + int context = -1; + int status = 0; + + if (! node) + return -1; + + /* For now, this function checks basic matcher attributes only; + * later, this may be turned into one of multiple AST visitors. */ + if (node->cmd == CONNECTION_FETCH) { + if (CONN_FETCH(node)->filter) + filter = CONN_FETCH(node)->filter->matcher; + context = CONN_FETCH(node)->type; + } + else if (node->cmd == CONNECTION_LIST) { + if (CONN_LIST(node)->filter) + filter = CONN_LIST(node)->filter->matcher; + context = CONN_LIST(node)->type; + } + else if (node->cmd == CONNECTION_LOOKUP) { + if (CONN_LOOKUP(node)->matcher) + m = CONN_LOOKUP(node)->matcher->matcher; + if (CONN_LOOKUP(node)->filter) + filter = CONN_LOOKUP(node)->filter->matcher; + context = CONN_LOOKUP(node)->type; + } + else if (node->cmd == CONNECTION_TIMESERIES) + return 0; + else + return -1; + + if (analyze_matcher(context, m)) + status = -1; + if (analyze_matcher(-1, filter)) + status = -1; + return status; +} /* sdb_fe_analyze */ + +/* vim: set tw=78 sw=4 ts=4 noexpandtab : */ + diff --git a/src/frontend/query.c b/src/frontend/query.c index 39b5d53..d6bd76a 100644 --- a/src/frontend/query.c +++ b/src/frontend/query.c @@ -114,7 +114,16 @@ sdb_fe_query(sdb_conn_t *conn) } if (node) { - status = sdb_fe_exec(conn, node); + if (sdb_fe_analyze(node)) { + char query[conn->cmd_len + 1]; + strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len); + query[sizeof(query) - 1] = '\0'; + sdb_log(SDB_LOG_ERR, "frontend: Failed to verify query '%s'", + query); + status = -1; + } + else + status = sdb_fe_exec(conn, node); sdb_object_deref(SDB_OBJ(node)); } @@ -176,6 +185,14 @@ sdb_fe_lookup(sdb_conn_t *conn) uint32_t type; int status; + conn_matcher_t m_node = { + { SDB_OBJECT_INIT, CONNECTION_MATCHER }, NULL + }; + conn_lookup_t node = { + { SDB_OBJECT_INIT, CONNECTION_LOOKUP }, + -1, &m_node, NULL + }; + if ((! conn) || (conn->cmd != CONNECTION_LOOKUP)) return -1; @@ -200,7 +217,19 @@ sdb_fe_lookup(sdb_conn_t *conn) return -1; } - status = sdb_fe_exec_lookup(conn, type, m, /* filter = */ NULL); + node.type = type; + m_node.matcher = m; + + if (sdb_fe_analyze(SDB_CONN_NODE(&node))) { + char expr[matcher_len + 1]; + strncpy(expr, matcher, sizeof(expr)); + expr[sizeof(expr) - 1] = '\0'; + sdb_log(SDB_LOG_ERR, "frontend: Failed to verify " + "lookup condition '%s'", expr); + status = -1; + } + else + status = sdb_fe_exec_lookup(conn, type, m, /* filter = */ NULL); sdb_object_deref(SDB_OBJ(m)); return status; } /* sdb_fe_lookup */ diff --git a/src/include/frontend/parser.h b/src/include/frontend/parser.h index d78ab20..e1c9c6f 100644 --- a/src/include/frontend/parser.h +++ b/src/include/frontend/parser.h @@ -29,6 +29,7 @@ #define SDB_FRONTEND_PARSER_H 1 #include "core/store.h" +#include "frontend/connection.h" #include "utils/llist.h" #ifdef __cplusplus @@ -69,6 +70,17 @@ sdb_fe_parse_matcher(const char *cond, int len); sdb_store_expr_t * sdb_fe_parse_expr(const char *expr, int len); +/* + * sdb_fe_analyze: + * Analyze a parsed node, checking for semantical errors. + * + * Returns: + * - 0 if the node is semantically correct + * - a negative value else + */ +int +sdb_fe_analyze(sdb_conn_node_t *node); + #ifdef __cplusplus } /* extern "C" */ #endif -- 2.30.2