Code

frontend: Added basic support for semantic AST analysis.
authorSebastian Harl <sh@tokkee.org>
Mon, 27 Oct 2014 23:08:07 +0000 (00:08 +0100)
committerSebastian Harl <sh@tokkee.org>
Mon, 27 Oct 2014 23:11:33 +0000 (00:11 +0100)
For now, the analyzer checks the types of all matcher operands.

src/Makefile.am
src/frontend/analyzer.c [new file with mode: 0644]
src/frontend/query.c
src/include/frontend/parser.h

index a69769d72dd21f159fea34daee3b6d540cae1bb3..c8ad524c12f6809cd01f7b37b7bac6f1df7128ab 100644 (file)
@@ -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 (file)
index 0000000..8c6b74d
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * SysDB - src/frontend/analyzer.c
+ * Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * 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 <core/store-private.h>
+#include <frontend/connection-private.h>
+#include <frontend/parser.h>
+
+#include <assert.h>
+
+/*
+ * 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 : */
+
index 39b5d53004da6774ad6395ade472e22cc33971b7..d6bd76a95ebb3c23ba06c291f324215233801ff2 100644 (file)
@@ -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 */
index d78ab20bade0e726e769fc595ec9c7bc0599306d..e1c9c6fa81e5d3b4e2b6eef91d14b8ed0fc3b3f9 100644 (file)
@@ -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