From dcae4c1b3d8a756ad894c15f36bce985bc09a2aa Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Sat, 25 Oct 2014 14:31:06 +0200 Subject: [PATCH] frontend/grammar: Changed ' ' to 'ANY '. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit … for services, metrics, and attributes. That is, let this involve less magic but make it clear that this looks at multiple objects and matches if any of them match. --- src/frontend/grammar.y | 85 ++++++++++++++++++++++------- src/frontend/scanner.l | 1 + t/integration/filter.sh | 4 +- t/integration/matching.sh | 8 +-- t/unit/core/store_lookup_test.c | 40 +++++++------- t/unit/frontend/parser_test.c | 96 ++++++++++++++++----------------- 6 files changed, 141 insertions(+), 93 deletions(-) diff --git a/src/frontend/grammar.y b/src/frontend/grammar.y index cfffdac..8057710 100644 --- a/src/frontend/grammar.y +++ b/src/frontend/grammar.y @@ -43,6 +43,20 @@ #include #include +/* + * private helper functions + */ + +static sdb_store_matcher_t * +name_matcher(char *type_name, char *cmp, sdb_store_expr_t *expr); + +static sdb_store_matcher_t * +name_iter_matcher(char *type_name, char *cmp, sdb_store_expr_t *expr); + +/* + * public API + */ + int sdb_fe_yylex(YYSTYPE *yylval, YYLTYPE *yylloc, sdb_fe_yyscan_t yyscanner); @@ -94,7 +108,7 @@ sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg); %token AND OR IS NOT MATCHING FILTER %token CMP_EQUAL CMP_NEQUAL CMP_REGEX CMP_NREGEX -%token CMP_LT CMP_LE CMP_GE CMP_GT IN +%token CMP_LT CMP_LE CMP_GE CMP_GT ANY IN %token CONCAT %token START END @@ -445,26 +459,16 @@ compare_matcher: | IDENTIFIER cmp expression { - int type = sdb_store_parse_object_type($1); - sdb_store_expr_t *e = sdb_store_expr_fieldvalue(SDB_FIELD_NAME); - sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op($2); - sdb_store_matcher_t *m; - assert(cb); - - m = cb(e, $3); - /* TODO: this only works as long as queries - * are limited to hosts */ - if (type == SDB_HOST) { - $$ = m; - } - else { - $$ = sdb_store_any_matcher(type, m); - sdb_object_deref(SDB_OBJ(m)); - } - + $$ = name_matcher($1, $2, $3); free($1); $1 = NULL; sdb_object_deref(SDB_OBJ($3)); - sdb_object_deref(SDB_OBJ(e)); + } + | + ANY IDENTIFIER cmp expression + { + $$ = name_iter_matcher($2, $3, $4); + free($2); $2 = NULL; + sdb_object_deref(SDB_OBJ($4)); } | expression IS NULL_T @@ -647,5 +651,48 @@ sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg) sdb_log(SDB_LOG_ERR, "frontend: parse error: %s", msg); } /* sdb_fe_yyerror */ +static sdb_store_matcher_t * +name_matcher(char *type_name, char *cmp, sdb_store_expr_t *expr) +{ + int type = sdb_store_parse_object_type(type_name); + sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op(cmp); + sdb_store_expr_t *e; + sdb_store_matcher_t *m; + assert(cb); + + /* TODO: this only works as long as queries + * are limited to hosts */ + if (type != SDB_HOST) + return NULL; + + e = sdb_store_expr_fieldvalue(SDB_FIELD_NAME); + m = cb(e, expr); + sdb_object_deref(SDB_OBJ(e)); + return m; +} /* name_matcher */ + +static sdb_store_matcher_t * +name_iter_matcher(char *type_name, char *cmp, sdb_store_expr_t *expr) +{ + int type = sdb_store_parse_object_type(type_name); + sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op(cmp); + sdb_store_expr_t *e; + sdb_store_matcher_t *m, *tmp; + assert(cb); + + /* TODO: this only works as long as queries + * are limited to hosts */ + if (type == SDB_HOST) { + return NULL; + } + + e = sdb_store_expr_fieldvalue(SDB_FIELD_NAME); + m = cb(e, expr); + tmp = sdb_store_any_matcher(type, m); + sdb_object_deref(SDB_OBJ(m)); + sdb_object_deref(SDB_OBJ(e)); + return tmp; +} /* name_iter_matcher */ + /* vim: set tw=78 sw=4 ts=4 noexpandtab : */ diff --git a/src/frontend/scanner.l b/src/frontend/scanner.l index 33b762c..4f09c37 100644 --- a/src/frontend/scanner.l +++ b/src/frontend/scanner.l @@ -52,6 +52,7 @@ static struct { int id; } reserved_words[] = { { "AND", AND }, + { "ANY", ANY }, { "END", END }, { "FETCH", FETCH }, { "FILTER", FILTER }, diff --git a/t/integration/filter.sh b/t/integration/filter.sh index fa21dd3..9c9fb0c 100755 --- a/t/integration/filter.sh +++ b/t/integration/filter.sh @@ -49,7 +49,7 @@ wait_for_sysdbd sleep 3 output="$( run_sysdb -H "$SOCKET_FILE" \ - -c "LOOKUP hosts MATCHING attribute != 'architecture' + -c "LOOKUP hosts MATCHING ANY attribute != 'architecture' FILTER .age >= 0s" )" echo "$output" \ | grep -F '"some.host.name"' \ @@ -59,7 +59,7 @@ echo "$output" | grep -F 'host1.example.com' && exit 1 echo "$output" | grep -F 'host2.example.com' && exit 1 output="$( run_sysdb -H "$SOCKET_FILE" \ - -c "LOOKUP hosts MATCHING attribute != 'architecture' + -c "LOOKUP hosts MATCHING ANY attribute != 'architecture' FILTER .last_update < 2Y" )" echo $output | grep -E '^\[\]$' diff --git a/t/integration/matching.sh b/t/integration/matching.sh index 67b9721..751b22b 100755 --- a/t/integration/matching.sh +++ b/t/integration/matching.sh @@ -49,7 +49,7 @@ wait_for_sysdbd sleep 3 output="$( run_sysdb -H "$SOCKET_FILE" \ - -c "LOOKUP hosts MATCHING metric = 'foo/bar/qux'" )" + -c "LOOKUP hosts MATCHING ANY metric = 'foo/bar/qux'" )" echo "$output" \ | grep -F '"some.host.name"' \ | grep -F '"other.host.name"' @@ -58,7 +58,7 @@ echo "$output" | grep -F 'host1.example.com' && exit 1 echo "$output" | grep -F 'host2.example.com' && exit 1 output="$( run_sysdb -H "$SOCKET_FILE" \ - -c "LOOKUP hosts MATCHING service = 'mock service'" )" + -c "LOOKUP hosts MATCHING ANY service = 'mock service'" )" echo "$output" \ | grep -F '"some.host.name"' \ | grep -F '"host1.example.com"' \ @@ -76,7 +76,7 @@ echo "$output" | grep -F 'other.host.name' && exit 1 echo "$output" | grep -F 'some.host.name' && exit 1 output="$( run_sysdb -H "$SOCKET_FILE" \ - -c "LOOKUP hosts MATCHING attribute != 'architecture'" )" + -c "LOOKUP hosts MATCHING ANY attribute != 'architecture'" )" echo "$output" \ | grep -F '"some.host.name"' \ | grep -F '"localhost"' @@ -85,7 +85,7 @@ echo "$output" | grep -F 'host1.example.com' && exit 1 echo "$output" | grep -F 'host2.example.com' && exit 1 output="$( run_sysdb -H "$SOCKET_FILE" \ - -c "LOOKUP hosts MATCHING service = 'sysdbd'" )" + -c "LOOKUP hosts MATCHING ANY service = 'sysdbd'" )" echo "$output" | grep -F '"localhost"' echo "$output" | grep -F 'some.host.name' && exit 1 echo "$output" | grep -F 'other.host.name' && exit 1 diff --git a/t/unit/core/store_lookup_test.c b/t/unit/core/store_lookup_test.c index dbf313a..3c99bb1 100644 --- a/t/unit/core/store_lookup_test.c +++ b/t/unit/core/store_lookup_test.c @@ -529,28 +529,28 @@ START_TEST(test_scan) { "host =~ 'a|b'", NULL, 2 }, { "host =~ 'host'", NULL, 0 }, { "host =~ '.'", NULL, 3 }, - { "metric = 'm1'", NULL, 2 }, - { "metric= 'm1'", "host = 'x'", 0 }, /* filter never matches */ - { "metric = 'm1'", + { "ANY metric = 'm1'", NULL, 2 }, + { "ANY metric= 'm1'", "host = 'x'", 0 }, /* filter never matches */ + { "ANY metric = 'm1'", "NOT attribute['x'] = ''", 2 }, /* filter always matches */ - { "metric =~ 'm'", NULL, 2 }, - { "metric !~ 'm'", NULL, 1 }, - { "metric =~ 'x'", NULL, 0 }, - { "service = 's1'", NULL, 2 }, - { "service = 's1'", "host = 'x'", 0 }, /* filter never matches */ - { "service = 's1'", + { "ANY metric =~ 'm'", NULL, 2 }, + { "ANY metric !~ 'm'", NULL, 1 }, + { "ANY metric =~ 'x'", NULL, 0 }, + { "ANY service = 's1'", NULL, 2 }, + { "ANY service = 's1'", "host = 'x'", 0 }, /* filter never matches */ + { "ANY service = 's1'", "NOT attribute['x'] = ''", 2 }, /* filter always matches */ - { "service =~ 's'", NULL, 2 }, - { "service !~ 's'", NULL, 1 }, - { "attribute = 'k1'", NULL, 2 }, - { "attribute = 'k1'", "host = 'x'", 0 }, /* filter never matches */ - { "attribute = 'k1'", + { "ANY service =~ 's'", NULL, 2 }, + { "ANY service !~ 's'", NULL, 1 }, + { "ANY attribute = 'k1'", NULL, 2 }, + { "ANY attribute = 'k1'", "host = 'x'",0 }, /* filter never matches */ + { "ANY attribute = 'k1'", "NOT attribute['x'] = ''", 2 }, /* filter always matches */ - { "attribute =~ 'k'", NULL, 2 }, - { "attribute =~ '1'", NULL, 2 }, - { "attribute =~ '2'", NULL, 1 }, - { "attribute = 'x'", NULL, 0 }, - { "attribute =~ 'x'", NULL, 0 }, + { "ANY attribute =~ 'k'", NULL, 2 }, + { "ANY attribute =~ '1'", NULL, 2 }, + { "ANY attribute =~ '2'", NULL, 1 }, + { "ANY attribute = 'x'", NULL, 0 }, + { "ANY attribute =~ 'x'", NULL, 0 }, { "attribute['k1'] = 'v1'", NULL, 1 }, { "attribute['k1'] =~ 'v1'", NULL, 1 }, { "attribute['k1'] =~ '^v1$'", NULL, 1 }, @@ -574,7 +574,7 @@ START_TEST(test_scan) { "attribute['k2'] != 123", NULL, 0 }, { "attribute['k1'] != 'v1'", NULL, 1 }, { "attribute['k1'] != 'v2'", NULL, 1 }, - { "attribute != 'x' " + { "ANY attribute != 'x' " "AND attribute['y'] !~ 'x'", NULL, 3 }, }; diff --git a/t/unit/frontend/parser_test.c b/t/unit/frontend/parser_test.c index 1ca9f2d..fd9e32f 100644 --- a/t/unit/frontend/parser_test.c +++ b/t/unit/frontend/parser_test.c @@ -65,10 +65,10 @@ START_TEST(test_parse) "host = 'host'", -1, 1, CONNECTION_LIST }, { "LIST services", -1, 1, CONNECTION_LIST }, { "LIST services FILTER " - "service = 'svc'", -1, 1, CONNECTION_LIST }, + "ANY service = 'svc'", -1, 1, CONNECTION_LIST }, { "LIST metrics", -1, 1, CONNECTION_LIST }, { "LIST metrics FILTER " - "metric = 'metric'", -1, 1, CONNECTION_LIST }, + "ANY metric = 'm'", -1, 1, CONNECTION_LIST }, { "LOOKUP hosts", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " @@ -77,18 +77,18 @@ START_TEST(test_parse) "host = 'host'", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " "host =~ 'p' AND " - "service =~ 'p'", -1, 1, CONNECTION_LOOKUP }, + "ANY service =~ 'p'", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING NOT " "host =~ 'p' AND " - "service =~ 'p'", -1, 1, CONNECTION_LOOKUP }, + "ANY service =~ 'p'", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " "host =~ 'p' AND " - "service =~ 'p' OR " - "service =~ 'r'", -1, 1, CONNECTION_LOOKUP }, + "ANY service =~ 'p' OR " + "ANY service =~ 'r'", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING NOT " "host =~ 'p' AND " - "service =~ 'p' OR " - "service =~ 'r'", -1, 1, CONNECTION_LOOKUP }, + "ANY service =~ 'p' OR " + "ANY service =~ 'r'", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " "host =~ 'p' " "FILTER .age > 1D", -1, 1, CONNECTION_LOOKUP }, @@ -189,7 +189,7 @@ START_TEST(test_parse) { "LOOKUP hosts MATCHING " "host IS NULL", -1, -1, 0 }, { "LOOKUP hosts MATCHING " - "service IS NULL", -1, -1, 0 }, + "ANY service IS NULL", -1, -1, 0 }, /* invalid numeric constants */ { "LOOKUP hosts MATCHING " @@ -318,39 +318,39 @@ START_TEST(test_parse_matcher) { "host = 'host' ", 13, MATCHER_EQ }, { "host &^ 'localhost'", -1, -1 }, /* match hosts by service */ - { "service < 'name'", -1, MATCHER_ANY }, - { "service <= 'name'", -1, MATCHER_ANY }, - { "service = 'name'", -1, MATCHER_ANY }, - { "service != 'name'", -1, MATCHER_ANY }, - { "service >= 'name'", -1, MATCHER_ANY }, - { "service > 'name'", -1, MATCHER_ANY }, - { "service =~ 'pattern'", -1, MATCHER_ANY }, - { "service !~ 'pattern'", -1, MATCHER_ANY }, - { "service &^ 'name'", -1, -1 }, + { "ANY service < 'name'", -1, MATCHER_ANY }, + { "ANY service <= 'name'", -1, MATCHER_ANY }, + { "ANY service = 'name'", -1, MATCHER_ANY }, + { "ANY service != 'name'", -1, MATCHER_ANY }, + { "ANY service >= 'name'", -1, MATCHER_ANY }, + { "ANY service > 'name'", -1, MATCHER_ANY }, + { "ANY service =~ 'pattern'", -1, MATCHER_ANY }, + { "ANY service !~ 'pattern'", -1, MATCHER_ANY }, + { "ANY service &^ 'name'", -1, -1 }, /* match hosts by metric */ - { "metric < 'name'", -1, MATCHER_ANY }, - { "metric <= 'name'", -1, MATCHER_ANY }, - { "metric = 'name'", -1, MATCHER_ANY }, - { "metric != 'name'", -1, MATCHER_ANY }, - { "metric >= 'name'", -1, MATCHER_ANY }, - { "metric > 'name'", -1, MATCHER_ANY }, - { "metric =~ 'pattern'", -1, MATCHER_ANY }, - { "metric !~ 'pattern'", -1, MATCHER_ANY }, + { "ANY metric < 'name'", -1, MATCHER_ANY }, + { "ANY metric <= 'name'", -1, MATCHER_ANY }, + { "ANY metric = 'name'", -1, MATCHER_ANY }, + { "ANY metric != 'name'", -1, MATCHER_ANY }, + { "ANY metric >= 'name'", -1, MATCHER_ANY }, + { "ANY metric > 'name'", -1, MATCHER_ANY }, + { "ANY metric =~ 'pattern'", -1, MATCHER_ANY }, + { "ANY metric !~ 'pattern'", -1, MATCHER_ANY }, /* match hosts by attribute */ - { "attribute < 'name'", -1, MATCHER_ANY }, - { "attribute <= 'name'", -1, MATCHER_ANY }, - { "attribute = 'name'", -1, MATCHER_ANY }, - { "attribute != 'name'", -1, MATCHER_ANY }, - { "attribute >= 'name'", -1, MATCHER_ANY }, - { "attribute > 'name'", -1, MATCHER_ANY }, - { "attribute =~ 'pattern'", -1, MATCHER_ANY }, - { "attribute !~ 'pattern'", -1, MATCHER_ANY }, - { "attribute &^ 'pattern'", -1, -1 }, + { "ANY attribute < 'name'", -1, MATCHER_ANY }, + { "ANY attribute <= 'name'", -1, MATCHER_ANY }, + { "ANY attribute = 'name'", -1, MATCHER_ANY }, + { "ANY attribute != 'name'", -1, MATCHER_ANY }, + { "ANY attribute >= 'name'", -1, MATCHER_ANY }, + { "ANY attribute > 'name'", -1, MATCHER_ANY }, + { "ANY attribute =~ 'pattern'", -1, MATCHER_ANY }, + { "ANY attribute !~ 'pattern'", -1, MATCHER_ANY }, + { "ANY attribute &^ 'pattern'", -1, -1 }, /* composite expressions */ { "host =~ 'pattern' AND " - "service =~ 'pattern'", -1, MATCHER_AND }, + "ANY service =~ 'pattern'", -1, MATCHER_AND }, { "host =~ 'pattern' OR " - "service =~ 'pattern'", -1, MATCHER_OR }, + "ANY service =~ 'pattern'", -1, MATCHER_OR }, { "NOT host = 'host'", -1, MATCHER_NOT }, /* numeric expressions */ { "attribute['foo'] < 123", -1, MATCHER_LT }, @@ -412,27 +412,27 @@ START_TEST(test_parse_matcher) /* check operator precedence */ { "host = 'name' OR " - "service = 'name' AND " - "attribute = 'name' OR " + "ANY service = 'name' AND " + "ANY attribute = 'name' OR " "attribute['foo'] = 'bar'", -1, MATCHER_OR }, { "host = 'name' AND " - "service = 'name' AND " - "attribute = 'name' OR " + "ANY service = 'name' AND " + "ANY attribute = 'name' OR " "attribute['foo'] = 'bar'", -1, MATCHER_OR }, { "host = 'name' AND " - "service = 'name' OR " - "attribute = 'name' AND " + "ANY service = 'name' OR " + "ANY attribute = 'name' AND " "attribute['foo'] = 'bar'", -1, MATCHER_OR }, { "(host = 'name' OR " - "service = 'name') AND " - "(attribute = 'name' OR " + "ANY service = 'name') AND " + "(ANY attribute = 'name' OR " "attribute['foo'] = 'bar')", -1, MATCHER_AND }, { "NOT host = 'name' OR " - "service = 'name'", -1, MATCHER_OR }, + "ANY service = 'name'", -1, MATCHER_OR }, { "NOT host = 'name' OR " - "NOT service = 'name'", -1, MATCHER_OR }, + "NOT ANY service = 'name'", -1, MATCHER_OR }, { "NOT (host = 'name' OR " - "NOT service = 'name')", -1, MATCHER_NOT }, + "NOT ANY service = 'name')", -1, MATCHER_NOT }, /* syntax errors */ { "LIST", -1, -1 }, -- 2.30.2