From: Sebastian Harl Date: Sun, 20 Jul 2014 18:55:44 +0000 (+0200) Subject: store, frontend: Added support for matching NULL attributes. X-Git-Tag: sysdb-0.3.0~41 X-Git-Url: https://git.tokkee.org/?p=sysdb.git;a=commitdiff_plain;h=444db0c3fae176a18166120dd19a7e01ee95823c store, frontend: Added support for matching NULL attributes. This matches any hosts which are missing the specified attribute. --- diff --git a/src/core/store-private.h b/src/core/store-private.h index c7d99f9..a2121f4 100644 --- a/src/core/store-private.h +++ b/src/core/store-private.h @@ -123,6 +123,7 @@ enum { MATCHER_EQ, MATCHER_GE, MATCHER_GT, + MATCHER_ISNULL, }; #define MATCHER_SYM(t) \ @@ -136,6 +137,7 @@ enum { : ((t) == MATCHER_EQ) ? "=" \ : ((t) == MATCHER_GE) ? ">=" \ : ((t) == MATCHER_GT) ? ">" \ + : ((t) == MATCHER_ISNULL) ? "IS NULL" \ : "UNKNOWN") /* match the name of something */ @@ -188,6 +190,12 @@ typedef struct { } attr_matcher_t; #define ATTR_M(m) ((attr_matcher_t *)(m)) +typedef struct { + sdb_store_matcher_t super; + char *attr_name; /* we only support matching attributes */ +} isnull_matcher_t; +#define ISNULL_M(m) ((isnull_matcher_t *)(m)) + /* match using conditionals */ typedef struct { sdb_store_matcher_t super; diff --git a/src/core/store_lookup.c b/src/core/store_lookup.c index 7e2acef..557b936 100644 --- a/src/core/store_lookup.c +++ b/src/core/store_lookup.c @@ -254,11 +254,19 @@ match_gt(sdb_store_matcher_t *m, sdb_host_t *host) return (status != INT_MAX) && (status > 0); } /* match_gt */ +static int +match_isnull(sdb_store_matcher_t *m, sdb_host_t *host) +{ + assert(m->type == MATCHER_ISNULL); + return attr_get(host, ISNULL_M(m)->attr_name) == NULL; +} /* match_isnull */ + typedef int (*matcher_cb)(sdb_store_matcher_t *, sdb_host_t *); /* this array needs to be indexable by the matcher types; * -> update the enum in store-private.h when updating this */ -static matcher_cb matchers[] = { +static matcher_cb +matchers[] = { match_logical, match_logical, match_unary, @@ -269,6 +277,7 @@ static matcher_cb matchers[] = { match_eq, match_ge, match_gt, + match_isnull, }; /* @@ -542,6 +551,39 @@ uop_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen) return buf; } /* uop_tostring */ +static int +isnull_matcher_init(sdb_object_t *obj, va_list ap) +{ + const char *name; + + M(obj)->type = va_arg(ap, int); + if (M(obj)->type != MATCHER_ISNULL) + return -1; + + name = va_arg(ap, const char *); + if (! name) + return -1; + ISNULL_M(obj)->attr_name = strdup(name); + if (! ISNULL_M(obj)->attr_name) + return -1; + return 0; +} /* isnull_matcher_init */ + +static void +isnull_matcher_destroy(sdb_object_t *obj) +{ + if (ISNULL_M(obj)->attr_name) + free(ISNULL_M(obj)->attr_name); + ISNULL_M(obj)->attr_name = NULL; +} /* isnull_matcher_destroy */ + +static char * +isnull_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen) +{ + snprintf(buf, buflen, "(IS NULL, attr.%s)", ISNULL_M(m)->attr_name); + return buf; +} /* isnull_tostring */ + static sdb_type_t name_type = { /* size = */ sizeof(name_matcher_t), /* init = */ name_matcher_init, @@ -572,11 +614,18 @@ static sdb_type_t uop_type = { /* destroy = */ uop_matcher_destroy, }; +static sdb_type_t isnull_type = { + /* size = */ sizeof(isnull_matcher_t), + /* init = */ isnull_matcher_init, + /* destroy = */ isnull_matcher_destroy, +}; + typedef char *(*matcher_tostring_cb)(sdb_store_matcher_t *, char *, size_t); /* this array needs to be indexable by the matcher types; * -> update the enum in store-private.h when updating this */ -static matcher_tostring_cb matchers_tostring[] = { +static matcher_tostring_cb +matchers_tostring[] = { op_tostring, op_tostring, uop_tostring, @@ -587,6 +636,7 @@ static matcher_tostring_cb matchers_tostring[] = { cond_tostring, cond_tostring, cond_tostring, + isnull_tostring, }; /* @@ -667,6 +717,13 @@ sdb_store_gt_matcher(sdb_store_cond_t *cond) MATCHER_GT, cond)); } /* sdb_store_gt_matcher */ +sdb_store_matcher_t * +sdb_store_isnull_matcher(const char *attr_name) +{ + return M(sdb_object_create("isnull-matcher", isnull_type, + MATCHER_ISNULL, attr_name)); +} /* sdb_store_isnull_matcher */ + static sdb_store_matcher_t * parse_attr_cmp(const char *attr, const char *op, const sdb_data_t *value) { @@ -680,7 +737,11 @@ parse_attr_cmp(const char *attr, const char *op, const sdb_data_t *value) if (! strcasecmp(attr, "name")) return NULL; - if (! strcasecmp(op, "<")) + if (! strcasecmp(op, "IS")) + return sdb_store_isnull_matcher(attr); + else if (! value) + return NULL; + else if (! strcasecmp(op, "<")) matcher = sdb_store_lt_matcher; else if (! strcasecmp(op, "<=")) matcher = sdb_store_le_matcher; diff --git a/src/frontend/grammar.y b/src/frontend/grammar.y index 34a9c6b..2173e6d 100644 --- a/src/frontend/grammar.y +++ b/src/frontend/grammar.y @@ -81,10 +81,13 @@ sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg); %token SCANNER_ERROR -%token AND OR NOT WHERE +%token AND OR IS NOT WHERE %token CMP_EQUAL CMP_NEQUAL CMP_REGEX CMP_NREGEX %token CMP_LT CMP_LE CMP_GE CMP_GT +/* NULL token */ +%token NULL_T + %token FETCH LIST LOOKUP %token IDENTIFIER STRING @@ -97,7 +100,8 @@ sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg); %right NOT %left CMP_EQUAL CMP_NEQUAL %left CMP_LT CMP_LE CMP_GE CMP_GT -%left CMP_REGEX CMP_NREGEX +%nonassoc CMP_REGEX CMP_NREGEX +%nonassoc IS %left '(' ')' %left '.' @@ -305,6 +309,25 @@ compare_matcher: free($3); $3 = NULL; sdb_data_free_datum(&$5); } + | + 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 + { + sdb_store_matcher_t *m; + m = sdb_store_matcher_parse_cmp($1, $3, "IS", NULL); + free($1); $1 = NULL; + free($3); $3 = NULL; + + /* sdb_store_inv_matcher return NULL if m==NULL */ + $$ = sdb_store_inv_matcher(m); + sdb_object_deref(SDB_OBJ(m)); + } ; op: diff --git a/src/frontend/scanner.l b/src/frontend/scanner.l index 0396db5..a7b72b4 100644 --- a/src/frontend/scanner.l +++ b/src/frontend/scanner.l @@ -108,12 +108,16 @@ float ({float1}|{float2}|{float3}|{float4}|{float5}) return AND; else if (! strcasecmp(yytext, "FETCH")) return FETCH; + else if (! strcasecmp(yytext, "IS")) + return IS; else if (! strcasecmp(yytext, "LIST")) return LIST; else if (! strcasecmp(yytext, "LOOKUP")) return LOOKUP; else if (! strcasecmp(yytext, "NOT")) return NOT; + else if (! strcasecmp(yytext, "NULL")) + return NULL_T; else if (! strcasecmp(yytext, "OR")) return OR; else if (! strcasecmp(yytext, "WHERE")) diff --git a/src/include/core/store.h b/src/include/core/store.h index aa28c96..7a9dc44 100644 --- a/src/include/core/store.h +++ b/src/include/core/store.h @@ -209,6 +209,13 @@ sdb_store_name_matcher(int type, const char *name, _Bool re); sdb_store_matcher_t * sdb_store_attr_matcher(const char *name, const char *value, _Bool re); +/* + * sdb_store_isnull_matcher: + * Creates a matcher matching "missing" attributes. + */ +sdb_store_matcher_t * +sdb_store_isnull_matcher(const char *attr_name); + /* * sdb_store_lt_matcher, sdb_store_le_matcher, sdb_store_eq_matcher, * sdb_store_ge_matcher, sdb_store_gt_matcher: diff --git a/t/unit/frontend/parser_test.c b/t/unit/frontend/parser_test.c index 8c62660..4b9b672 100644 --- a/t/unit/frontend/parser_test.c +++ b/t/unit/frontend/parser_test.c @@ -106,6 +106,22 @@ START_TEST(test_parse) "attribute.foo = " "-12e+3", -1, 1, CONNECTION_LOOKUP }, + /* NULL */ + { "LOOKUP hosts WHERE " + "attribute.foo " + "IS NULL", -1, 1, CONNECTION_LOOKUP }, + { "LOOKUP hosts WHERE " + "attribute.foo " + "IS NOT NULL", -1, 1, CONNECTION_LOOKUP }, + { "LOOKUP hosts WHERE " + "NOT attribute.foo " + "IS NULL", -1, 1, CONNECTION_LOOKUP }, + { "LOOKUP hosts WHERE " + "host.name IS NULL", -1, -1, 0 }, + { "LOOKUP hosts WHERE " + "service.name " + "IS NULL", -1, -1, 0 }, + /* invalid numeric constants */ { "LOOKUP hosts WHERE " "attribute.foo = " @@ -210,6 +226,16 @@ START_TEST(test_parse_matcher) { "host.name =~ 'pattern' OR " "service.name =~ 'pattern'", -1, MATCHER_OR }, { "NOT host.name = '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 }, + /* 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 }, /* check operator precedence */ { "host.name = 'name' OR "