Code

Add support for the 'NOT IN' operator.
authorSebastian Harl <sh@tokkee.org>
Sun, 1 Mar 2015 15:39:20 +0000 (16:39 +0100)
committerSebastian Harl <sh@tokkee.org>
Sun, 1 Mar 2015 15:39:20 +0000 (16:39 +0100)
'a NOT IN b' is the same as 'NOT a IN b'.

doc/sysdbql.7.txt
src/core/store-private.h
src/core/store_lookup.c
src/frontend/analyzer.c
src/frontend/grammar.y
src/include/core/store.h
t/unit/frontend/parser_test.c

index c0cecba8dfe55cbaafe839139c6ecd19510fcadf..7ed9a11ec61baf7ad6b9db02b70b7d26465cb639 100644 (file)
@@ -130,12 +130,12 @@ Boolean expressions may use the following operators:
        when accessing an attribute value).
 
 '<expression>' *IN* '<expression>'::
+'<expression>' *NOT IN* '<expression>'::
        Checks whether the value of the first expression is included in the value
-       of the second expression which has to be an array value (e.g., *backend*
-       field). If the second value is not an array or if the type of the first
-       value does not match the array's element type, the expression always
-       evaluates to false. The first value may also be an array. In this case,
-       the expression evaluates to true if all elements of that array are
+       of the second expression (or not). The second value has to be an array
+       value (e.g., *backend* field) and the type of the first value has to match
+       the array's element type. The first value may also be an array. In this
+       case, the expression evaluates to true if all elements of that array are
        included in the second array where order does not matter.
 
 Parentheses ('()') may be used around subexpressions to group them and enforce
index 97e8a6c2fcce7fff076b2f0275ead0a2ae42306a..4ef11f917ac22eb875a912c7839868c99c9d54dd 100644 (file)
@@ -147,6 +147,7 @@ enum {
        MATCHER_ANY,
        MATCHER_ALL,
        MATCHER_IN,
+       MATCHER_NIN,
 
        /* unary operators */
        MATCHER_ISNULL,
@@ -170,6 +171,7 @@ enum {
                : ((t) == MATCHER_ANY) ? "ANY" \
                : ((t) == MATCHER_ALL) ? "ALL" \
                : ((t) == MATCHER_IN) ? "IN" \
+               : ((t) == MATCHER_NIN) ? "NOT IN" \
                : ((t) == MATCHER_ISNULL) ? "IS NULL" \
                : ((t) == MATCHER_ISNNULL) ? "IS NOT NULL" \
                : ((t) == MATCHER_LT) ? "<" \
index a95f5877d38011dff4788e4655e8616bc27d420a..2d1b3ca11e5fc895ddf7fca662412e345ba84e32 100644 (file)
@@ -326,7 +326,7 @@ match_in(sdb_store_matcher_t *m, sdb_store_obj_t *obj,
        sdb_data_t value = SDB_DATA_INIT, array = SDB_DATA_INIT;
        int status = 1;
 
-       assert(m->type == MATCHER_IN);
+       assert((m->type == MATCHER_IN) || (m->type == MATCHER_NIN));
 
        if (expr_eval2(CMP_M(m)->left, &value,
                                CMP_M(m)->right, &array, obj, filter))
@@ -336,6 +336,8 @@ match_in(sdb_store_matcher_t *m, sdb_store_obj_t *obj,
                status = sdb_data_inarray(&value, &array);
 
        expr_free_datum2(CMP_M(m)->left, &value, CMP_M(m)->right, &array);
+       if (m->type == MATCHER_NIN)
+               return !status;
        return status;
 } /* match_in */
 
@@ -401,6 +403,7 @@ matchers[] = {
        match_iter,
        match_iter,
        match_in,
+       match_in,
 
        /* unary operators */
        match_isnull,
@@ -633,6 +636,13 @@ sdb_store_in_matcher(sdb_store_expr_t *left, sdb_store_expr_t *right)
                                MATCHER_IN, left, right));
 } /* sdb_store_in_matcher */
 
+sdb_store_matcher_t *
+sdb_store_nin_matcher(sdb_store_expr_t *left, sdb_store_expr_t *right)
+{
+       return M(sdb_object_create("not-in-matcher", cmp_type,
+                               MATCHER_NIN, left, right));
+} /* sdb_store_in_matcher */
+
 sdb_store_matcher_t *
 sdb_store_regex_matcher(sdb_store_expr_t *left, sdb_store_expr_t *right)
 {
index 4e29fdf85fb956312466d50b514e3bae984fb135..96ffe6250cbe8d23b7cb28f8672a9b9a9e3f7dce 100644 (file)
@@ -251,6 +251,7 @@ analyze_matcher(int context, sdb_store_matcher_t *m, sdb_strbuf_t *errbuf)
                        break;
 
                case MATCHER_IN:
+               case MATCHER_NIN:
                        if (analyze_expr(context, CMP_M(m)->left, errbuf))
                                return -1;
                        if (analyze_expr(context, CMP_M(m)->right, errbuf))
index 87184a4e15ef3effafe1810c11e368b529b14ea8..323fe4feadba40715c3b43c95d0f5d93441de564 100644 (file)
@@ -565,6 +565,13 @@ compare_matcher:
                        sdb_object_deref(SDB_OBJ($1));
                        sdb_object_deref(SDB_OBJ($3));
                }
+       |
+       expression NOT IN expression
+               {
+                       $$ = sdb_store_nin_matcher($1, $4);
+                       sdb_object_deref(SDB_OBJ($1));
+                       sdb_object_deref(SDB_OBJ($4));
+               }
        ;
 
 expression:
index 30ad0ab386b81517128c47fbe0995d8dcf030c86..68f094b4a9d996a85fb40292656e9fcad4bd8cff 100644 (file)
@@ -471,6 +471,14 @@ sdb_store_all_matcher(int type, sdb_store_matcher_t *m);
 sdb_store_matcher_t *
 sdb_store_in_matcher(sdb_store_expr_t *left, sdb_store_expr_t *right);
 
+/*
+ * sdb_store_nin_matcher:
+ * Like sdb_store_in_matcher but matches if the left value is not included in
+ * the right value.
+ */
+sdb_store_matcher_t *
+sdb_store_nin_matcher(sdb_store_expr_t *left, sdb_store_expr_t *right);
+
 /*
  * sdb_store_lt_matcher, sdb_store_le_matcher, sdb_store_eq_matcher,
  * sdb_store_ge_matcher, sdb_store_gt_matcher:
index 599ffe5088e9cc06b8fce8df51081b9b92ab4f42..2bbf5ccaed1402d12eded1311d0b2d26f358b374 100644 (file)
@@ -255,6 +255,8 @@ struct {
        /* array iteration */
        { "LOOKUP hosts MATCHING "
          "'foo' IN backend",   -1,   1, SDB_CONNECTION_LOOKUP },
+       { "LOOKUP hosts MATCHING 'foo' "
+         "NOT IN backend",   -1,   1, SDB_CONNECTION_LOOKUP },
        { "LOOKUP hosts MATCHING "
          "['foo','bar'] "
          "IN backend ",        -1,   1, SDB_CONNECTION_LOOKUP },
@@ -265,6 +267,8 @@ struct {
        /* type mismatch */
        { "LOOKUP hosts MATCHING "
          "1 IN backend ",      -1,  -1, 0 },
+       { "LOOKUP hosts MATCHING "
+         "1 NOT IN backend ",  -1,  -1, 0 },
        { "LOOKUP hosts MATCHING "
          "ANY backend < 'b'",  -1,   1, SDB_CONNECTION_LOOKUP },
        { "LOOKUP hosts MATCHING "
@@ -691,6 +695,9 @@ struct {
        { "interval >= 20s",              -1,  MATCHER_GE },
        { "interval > 20s",               -1,  MATCHER_GT },
        { "'be' IN backend",              -1,  MATCHER_IN },
+       { "'be' NOT IN backend",          -1,  MATCHER_NIN },
+       { "['a','b'] IN backend",         -1,  MATCHER_IN },
+       { "['a','b'] NOT IN backend",     -1,  MATCHER_NIN },
 
        /* check operator precedence */
        { "name = 'name' OR "