From f5f3eb3a9bdc2e0e4efae16a761453e44f35e646 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Mon, 6 Oct 2014 08:18:10 +0200 Subject: [PATCH] frontend/grammar: Access attribute values using attributes[]. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit … instead of attribute.. We'll use . for all directly accessible object fields instead. --- src/frontend/grammar.y | 11 ++- t/integration/simple_query.sh | 4 +- t/unit/core/store_lookup_test.c | 70 +++++++------- t/unit/frontend/parser_test.c | 156 ++++++++++++++++---------------- 4 files changed, 121 insertions(+), 120 deletions(-) diff --git a/src/frontend/grammar.y b/src/frontend/grammar.y index f13bbad..08f8414 100644 --- a/src/frontend/grammar.y +++ b/src/frontend/grammar.y @@ -113,6 +113,7 @@ sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg); %nonassoc IS %left '+' '-' %left '*' '/' '%' +%left '[' ']' %left '(' ')' %left '.' @@ -411,22 +412,22 @@ compare_matcher: sdb_object_deref(SDB_OBJ($3)); } | - IDENTIFIER '.' IDENTIFIER op expression + IDENTIFIER '[' IDENTIFIER ']' op expression { - $$ = sdb_store_matcher_parse_cmp($1, $3, $4, $5); + $$ = sdb_store_matcher_parse_cmp($1, $3, $5, $6); free($1); $1 = NULL; free($3); $3 = NULL; - sdb_object_deref(SDB_OBJ($5)); + sdb_object_deref(SDB_OBJ($6)); } | - IDENTIFIER '.' IDENTIFIER IS NULL_T + IDENTIFIER '[' IDENTIFIER ']' IS NULL_T { $$ = sdb_store_matcher_parse_cmp($1, $3, "IS", NULL); free($1); $1 = NULL; free($3); $3 = NULL; } | - IDENTIFIER '.' IDENTIFIER IS NOT NULL_T + IDENTIFIER '[' IDENTIFIER ']' IS NOT NULL_T { sdb_store_matcher_t *m; m = sdb_store_matcher_parse_cmp($1, $3, "IS", NULL); diff --git a/t/integration/simple_query.sh b/t/integration/simple_query.sh index 35a55ce..1e1028c 100755 --- a/t/integration/simple_query.sh +++ b/t/integration/simple_query.sh @@ -120,7 +120,7 @@ echo "$output" | grep -F 'localhost' && exit 1 echo "$output" | grep -F 'other.host.name' && exit 1 output="$( run_sysdb -H "$SOCKET_FILE" \ - -c "LOOKUP hosts MATCHING attribute.architecture = 'x42'" )" + -c "LOOKUP hosts MATCHING attribute[architecture] = 'x42'" )" echo "$output" \ | grep -F '"host1.example.com"' \ | grep -F '"host2.example.com"' @@ -183,7 +183,7 @@ echo "$output" | grep -F 'localhost' && exit 1 # When querying hosts that don't exist, expect a zero exit code. output="$( run_sysdb -H "$SOCKET_FILE" \ - -c "LOOKUP hosts MATCHING attribute.invalid = 'none'" )" + -c "LOOKUP hosts MATCHING attribute[invalid] = 'none'" )" echo $output | grep -E '^\[\]$' run_sysdb -H "$SOCKET_FILE" \ diff --git a/t/unit/core/store_lookup_test.c b/t/unit/core/store_lookup_test.c index a2b1595..af680f0 100644 --- a/t/unit/core/store_lookup_test.c +++ b/t/unit/core/store_lookup_test.c @@ -773,80 +773,80 @@ START_TEST(test_scan) int expected; const char *tostring_re; } golden_data[] = { - { "host = 'a'", NULL, 1, + { "host = 'a'", NULL, 1, "OBJ\\[host\\]\\{ NAME\\{ 'a', \\(nil\\) \\} \\}" }, - { "host = 'a'", "host = 'x'", 0, /* filter never matches */ + { "host = 'a'", "host = 'x'", 0, /* filter never matches */ "OBJ\\[host\\]\\{ NAME\\{ 'a', \\(nil\\) \\} \\}" }, { "host = 'a'", - "NOT attribute.x = ''", 1, /* filter always matches */ + "NOT attribute[x] = ''", 1, /* filter always matches */ "OBJ\\[host\\]\\{ NAME\\{ 'a', \\(nil\\) \\} \\}" }, - { "host =~ 'a|b'", NULL, 2, + { "host =~ 'a|b'", NULL, 2, "OBJ\\[host\\]\\{ NAME\\{ NULL, "PTR_RE" \\} \\}" }, - { "host =~ 'host'", NULL, 0, + { "host =~ 'host'", NULL, 0, "OBJ\\[host\\]\\{ NAME\\{ NULL, "PTR_RE" \\} \\}" }, - { "host =~ '.'", NULL, 3, + { "host =~ '.'", NULL, 3, "OBJ\\[host\\]\\{ NAME\\{ NULL, "PTR_RE" \\} \\}" }, - { "metric = 'm1'", NULL, 2, + { "metric = 'm1'", NULL, 2, "OBJ\\[metric\\]\\{ NAME\\{ 'm1', \\(nil\\) } \\}" }, - { "metric= 'm1'", "host = 'x'", 0, /* filter never matches */ + { "metric= 'm1'", "host = 'x'", 0, /* filter never matches */ "OBJ\\[metric\\]\\{ NAME\\{ 'm1', \\(nil\\) } \\}" }, { "metric = 'm1'", - "NOT attribute.x = ''", 2, /* filter always matches */ + "NOT attribute[x] = ''", 2, /* filter always matches */ "OBJ\\[metric\\]\\{ NAME\\{ 'm1', \\(nil\\) } \\}" }, - { "metric =~ 'm'", NULL, 2, + { "metric =~ 'm'", NULL, 2, "OBJ\\[metric\\]\\{ NAME\\{ NULL, "PTR_RE" } \\}" }, - { "metric !~ 'm'", NULL, 1, + { "metric !~ 'm'", NULL, 1, "\\(NOT, OBJ\\[metric\\]\\{ NAME\\{ NULL, "PTR_RE" } \\}\\)" }, - { "metric =~ 'x'", NULL, 0, + { "metric =~ 'x'", NULL, 0, "OBJ\\[metric\\]\\{ NAME\\{ NULL, "PTR_RE" } \\}" }, - { "service = 's1'", NULL, 2, + { "service = 's1'", NULL, 2, "OBJ\\[service\\]\\{ NAME\\{ 's1', \\(nil\\) } \\}" }, - { "service = 's1'", "host = 'x'", 0, /* filter never matches */ + { "service = 's1'", "host = 'x'", 0, /* filter never matches */ "OBJ\\[service\\]\\{ NAME\\{ 's1', \\(nil\\) } \\}" }, { "service = 's1'", - "NOT attribute.x = ''", 2, /* filter always matches */ + "NOT attribute[x] = ''", 2, /* filter always matches */ "OBJ\\[service\\]\\{ NAME\\{ 's1', \\(nil\\) } \\}" }, - { "service =~ 's'", NULL, 2, + { "service =~ 's'", NULL, 2, "OBJ\\[service\\]\\{ NAME\\{ NULL, "PTR_RE" } \\}" }, - { "service !~ 's'", NULL, 1, + { "service !~ 's'", NULL, 1, "\\(NOT, OBJ\\[service\\]\\{ NAME\\{ NULL, "PTR_RE" } \\}\\)" }, - { "attribute = 'k1'", NULL, 1, + { "attribute = 'k1'", NULL, 1, "OBJ\\[attribute\\]\\{ NAME\\{ 'k1', \\(nil\\) \\} " }, - { "attribute = 'k1'", "host = 'x'", 0, /* filter never matches */ + { "attribute = 'k1'", "host = 'x'", 0, /* filter never matches */ "OBJ\\[attribute\\]\\{ NAME\\{ 'k1', \\(nil\\) \\} " }, { "attribute = 'k1'", - "NOT attribute.x = ''", 1, /* filter always matches */ + "NOT attribute[x] = ''", 1, /* filter always matches */ "OBJ\\[attribute\\]\\{ NAME\\{ 'k1', \\(nil\\) \\} " }, - { "attribute = 'x'", NULL, 0, + { "attribute = 'x'", NULL, 0, "OBJ\\[attribute\\]\\{ NAME\\{ 'x', \\(nil\\) \\}" }, - { "attribute.k1 = 'v1'", NULL, 1, + { "attribute[k1] = 'v1'", NULL, 1, "ATTR\\[k1\\]\\{ VALUE\\{ 'v1', \\(nil\\) \\} \\}" }, - { "attribute.k1 IS NULL", NULL, 2, + { "attribute[k1] IS NULL", NULL, 2, "\\(IS NULL, ATTR\\[k1\\]\\)" }, - { "attribute.x1 IS NULL", NULL, 3, + { "attribute[x1] IS NULL", NULL, 3, "\\(IS NULL, ATTR\\[x1\\]\\)" }, - { "attribute.k1 IS NOT NULL", NULL, 1, + { "attribute[k1] IS NOT NULL", NULL, 1, "\\(NOT, \\(IS NULL, ATTR\\[k1\\]\\)\\)" }, - { "attribute.x1 IS NOT NULL", NULL, 0, + { "attribute[x1] IS NOT NULL", NULL, 0, "\\(NOT, \\(IS NULL, ATTR\\[x1\\]\\)\\)" }, - { "attribute.k2 < 123", NULL, 0, + { "attribute[k2] < 123", NULL, 0, "ATTR\\[k2\\]\\{ < 123 \\}" }, - { "attribute.k2 <= 123", NULL, 1, + { "attribute[k2] <= 123", NULL, 1, "ATTR\\[k2\\]\\{ <= 123 \\}" }, - { "attribute.k2 >= 123", NULL, 1, + { "attribute[k2] >= 123", NULL, 1, "ATTR\\[k2\\]\\{ >= 123 \\}" }, - { "attribute.k2 > 123", NULL, 0, + { "attribute[k2] > 123", NULL, 0, "ATTR\\[k2\\]\\{ > 123 \\}" }, - { "attribute.k2 = 123", NULL, 1, + { "attribute[k2] = 123", NULL, 1, "ATTR\\[k2\\]\\{ = 123 \\}" }, - { "attribute.k2 != 123", NULL, 2, + { "attribute[k2] != 123", NULL, 2, "\\(NOT, ATTR\\[k2\\]\\{ = 123 \\}\\)" }, - { "attribute.k1 != 'v1'", NULL, 2, + { "attribute[k1] != 'v1'", NULL, 2, "\\(NOT, ATTR\\[k1\\]\\{ VALUE\\{ 'v1', \\(nil\\) \\} \\}\\)" }, - { "attribute.k1 != 'v2'", NULL, 3, + { "attribute[k1] != 'v2'", NULL, 3, "\\(NOT, ATTR\\[k1\\]\\{ VALUE\\{ 'v2', \\(nil\\) \\} \\}\\)" }, { "attribute != 'x' " - "AND attribute.y !~ 'x'", NULL, 3, + "AND attribute[y] !~ 'x'", NULL, 3, "\\(AND, " "\\(NOT, OBJ\\[attribute\\]\\{ NAME\\{ 'x', \\(nil\\) \\} \\}\\), " "\\(NOT, ATTR\\[y\\]\\{ VALUE\\{ NULL, "PTR_RE" \\} \\}\\)\\)" }, diff --git a/t/unit/frontend/parser_test.c b/t/unit/frontend/parser_test.c index 4ee4d2f..1412ee5 100644 --- a/t/unit/frontend/parser_test.c +++ b/t/unit/frontend/parser_test.c @@ -129,58 +129,58 @@ START_TEST(test_parse) /* numeric constants */ { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "1234", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "attribute.foo != " + "attribute[foo] != " "+234", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "attribute.foo < " + "attribute[foo] < " "-234", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "attribute.foo > " + "attribute[foo] > " "12.4", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "attribute.foo <= " + "attribute[foo] <= " "12. + .3", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "attribute.foo >= " + "attribute[foo] >= " ".4", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "+12e3", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "+12e-3", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "-12e+3", -1, 1, CONNECTION_LOOKUP }, /* date, time, interval constants */ { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "1 Y 42D", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "1s 42D", -1, 1, CONNECTION_LOOKUP }, /* * TODO: Something like 1Y42D should work as well but it doesn't since * the scanner will tokenize it into {digit}{identifier} :-/ * { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "1Y42D", -1, 1, CONNECTION_LOOKUP }, */ /* NULL */ { "LOOKUP hosts MATCHING " - "attribute.foo " + "attribute[foo] " "IS NULL", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "attribute.foo " + "attribute[foo] " "IS NOT NULL", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " - "NOT attribute.foo " + "NOT attribute[foo] " "IS NULL", -1, 1, CONNECTION_LOOKUP }, { "LOOKUP hosts MATCHING " "host IS NULL", -1, -1, 0 }, @@ -189,25 +189,25 @@ START_TEST(test_parse) /* invalid numeric constants */ { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "+-12e+3", -1, -1, 0 }, { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "-12e-+3", -1, -1, 0 }, { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "e+3", -1, -1, 0 }, { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "3e", -1, -1, 0 }, /* following SQL standard, we don't support hex numbers */ { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "0x12", -1, -1, 0 }, /* invalid expressions */ { "LOOKUP hosts MATCHING " - "attribute.foo = " + "attribute[foo] = " "1.23 + 'foo'", -1, -1, 0 }, /* comments */ @@ -288,95 +288,95 @@ START_TEST(test_parse_matcher) int expected; } golden_data[] = { /* empty expressions */ - { NULL, -1, -1 }, - { "", -1, -1 }, + { NULL, -1, -1 }, + { "", -1, -1 }, /* valid expressions */ - { "host = 'localhost'", -1, MATCHER_NAME }, - { "host != 'localhost'", -1, MATCHER_NOT }, - { "host =~ 'host'", -1, MATCHER_NAME }, - { "host !~ 'host'", -1, MATCHER_NOT }, - { "host = 'localhost' -- foo", -1, MATCHER_NAME }, - { "host = 'host' ", 13, MATCHER_NAME }, + { "host = 'localhost'", -1, MATCHER_NAME }, + { "host != 'localhost'", -1, MATCHER_NOT }, + { "host =~ 'host'", -1, MATCHER_NAME }, + { "host !~ 'host'", -1, MATCHER_NOT }, + { "host = 'localhost' -- foo", -1, MATCHER_NAME }, + { "host = 'host' ", 13, MATCHER_NAME }, /* match hosts by service */ - { "service = 'name'", -1, MATCHER_NAME }, - { "service != 'name'", -1, MATCHER_NOT }, - { "service =~ 'pattern'", -1, MATCHER_NAME }, - { "service !~ 'pattern'", -1, MATCHER_NOT }, + { "service = 'name'", -1, MATCHER_NAME }, + { "service != 'name'", -1, MATCHER_NOT }, + { "service =~ 'pattern'", -1, MATCHER_NAME }, + { "service !~ 'pattern'", -1, MATCHER_NOT }, /* match hosts by attribute */ - { "attribute = 'name'", -1, MATCHER_NAME }, - { "attribute != 'name'", -1, MATCHER_NOT }, - { "attribute =~ 'pattern'", -1, MATCHER_NAME }, - { "attribute !~ 'pattern'", -1, MATCHER_NOT }, + { "attribute = 'name'", -1, MATCHER_NAME }, + { "attribute != 'name'", -1, MATCHER_NOT }, + { "attribute =~ 'pattern'", -1, MATCHER_NAME }, + { "attribute !~ 'pattern'", -1, MATCHER_NOT }, /* composite expressions */ { "host =~ 'pattern' AND " - "service =~ 'pattern'", -1, MATCHER_AND }, + "service =~ 'pattern'", -1, MATCHER_AND }, { "host =~ 'pattern' OR " - "service =~ 'pattern'", -1, MATCHER_OR }, - { "NOT host = 'host'", -1, MATCHER_NOT }, + "service =~ 'pattern'", -1, MATCHER_OR }, + { "NOT host = 'host'", -1, MATCHER_NOT }, /* numeric expressions */ - { "attribute.foo < 123", -1, MATCHER_LT }, - { "attribute.foo <= 123", -1, MATCHER_LE }, - { "attribute.foo = 123", -1, MATCHER_EQ }, - { "attribute.foo >= 123", -1, MATCHER_GE }, - { "attribute.foo > 123", -1, MATCHER_GT }, + { "attribute[foo] < 123", -1, MATCHER_LT }, + { "attribute[foo] <= 123", -1, MATCHER_LE }, + { "attribute[foo] = 123", -1, MATCHER_EQ }, + { "attribute[foo] >= 123", -1, MATCHER_GE }, + { "attribute[foo] > 123", -1, MATCHER_GT }, /* datetime expressions */ - { "attribute.foo = " - "2014-08-16", -1, MATCHER_EQ }, - { "attribute.foo = " - "17:23", -1, MATCHER_EQ }, - { "attribute.foo = " - "17:23:53", -1, MATCHER_EQ }, - { "attribute.foo = " - "17:23:53.123", -1, MATCHER_EQ }, - { "attribute.foo = " - "17:23:53.123456789", -1, MATCHER_EQ }, - { "attribute.foo = " - "2014-08-16 17:23", -1, MATCHER_EQ }, - { "attribute.foo = " - "2014-08-16 17:23:53", -1, MATCHER_EQ }, + { "attribute[foo] = " + "2014-08-16", -1, MATCHER_EQ }, + { "attribute[foo] = " + "17:23", -1, MATCHER_EQ }, + { "attribute[foo] = " + "17:23:53", -1, MATCHER_EQ }, + { "attribute[foo] = " + "17:23:53.123", -1, MATCHER_EQ }, + { "attribute[foo] = " + "17:23:53.123456789", -1, MATCHER_EQ }, + { "attribute[foo] = " + "2014-08-16 17:23", -1, MATCHER_EQ }, + { "attribute[foo] = " + "2014-08-16 17:23:53", -1, MATCHER_EQ }, /* NULL; while this is an implementation detail, * IS NULL currently maps to an equality matcher */ - { "attribute.foo IS NULL", -1, MATCHER_ISNULL }, - { "attribute.foo IS NOT NULL", -1, MATCHER_NOT }, + { "attribute[foo] IS NULL", -1, MATCHER_ISNULL }, + { "attribute[foo] IS NOT NULL", -1, MATCHER_NOT }, /* object field matchers */ - { ":last_update < 10s", -1, MATCHER_LT }, - { ":AGE <= 1m", -1, MATCHER_LE }, - { ":interval = 10h", -1, MATCHER_EQ }, - { ":Last_Update >= 24D", -1, MATCHER_GE }, - { ":age > 1M", -1, MATCHER_GT }, - { ":age != 20Y", -1, MATCHER_NOT }, - { ":backend != 'be'", -1, MATCHER_NOT }, - { ":age <= 2 * :interval", -1, MATCHER_LE }, + { ":last_update < 10s", -1, MATCHER_LT }, + { ":AGE <= 1m", -1, MATCHER_LE }, + { ":interval = 10h", -1, MATCHER_EQ }, + { ":Last_Update >= 24D", -1, MATCHER_GE }, + { ":age > 1M", -1, MATCHER_GT }, + { ":age != 20Y", -1, MATCHER_NOT }, + { ":backend != 'be'", -1, MATCHER_NOT }, + { ":age <= 2 * :interval", -1, MATCHER_LE }, /* check operator precedence */ { "host = 'name' OR " "service = 'name' AND " "attribute = 'name' OR " - "attribute.foo = 'bar'", -1, MATCHER_OR }, + "attribute[foo] = 'bar'", -1, MATCHER_OR }, { "host = 'name' AND " "service = 'name' AND " "attribute = 'name' OR " - "attribute.foo = 'bar'", -1, MATCHER_OR }, + "attribute[foo] = 'bar'", -1, MATCHER_OR }, { "host = 'name' AND " "service = 'name' OR " "attribute = 'name' AND " - "attribute.foo = 'bar'", -1, MATCHER_OR }, + "attribute[foo] = 'bar'", -1, MATCHER_OR }, { "(host = 'name' OR " "service = 'name') AND " "(attribute = 'name' OR " - "attribute.foo = 'bar')", -1, MATCHER_AND }, + "attribute[foo] = 'bar')", -1, MATCHER_AND }, { "NOT host = 'name' OR " - "service = 'name'", -1, MATCHER_OR }, + "service = 'name'", -1, MATCHER_OR }, { "NOT host = 'name' OR " - "NOT service = 'name'", -1, MATCHER_OR }, + "NOT service = 'name'", -1, MATCHER_OR }, { "NOT (host = 'name' OR " - "NOT service = 'name')", -1, MATCHER_NOT }, + "NOT service = 'name')", -1, MATCHER_NOT }, /* syntax errors */ - { "LIST", -1, -1 }, - { "foo &^ bar", -1, -1 }, + { "LIST", -1, -1 }, + { "foo &^ bar", -1, -1 }, }; size_t i; -- 2.30.2