]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: enum mail_search_arg_type - Add SEARCH_NIL
authorMarco Bettini <marco.bettini@open-xchange.com>
Thu, 20 Oct 2022 14:07:23 +0000 (14:07 +0000)
committerMarco Bettini <marco.bettini@open-xchange.com>
Wed, 16 Nov 2022 15:03:56 +0000 (15:03 +0000)
src/lib-storage/index/imapc/imapc-search.c
src/lib-storage/index/index-search.c
src/lib-storage/mail-search-args-cmdline.c
src/lib-storage/mail-search-args-imap.c
src/lib-storage/mail-search-args-simplify.c
src/lib-storage/mail-search-register-imap.c
src/lib-storage/mail-search.c
src/lib-storage/mail-search.h
src/lib-storage/test-mail-search-args-simplify.c

index e395919f3429d70accc65eacca55ac3aa6f167c9..98fa8b5662e1e660db9513c4585ede0ba94bc89a 100644 (file)
@@ -145,6 +145,8 @@ imapc_build_search_query_arg(struct imapc_mailbox *mbox,
        case SEARCH_MIMEPART:
                /* not supported for now */
                break;
+       case SEARCH_NIL:
+               i_unreached();
        }
        return FALSE;
 }
index dec52f30742c6c61b9c1d5bbc20ec21ed6e49491..05eaa399a8066daebafb09a5a0fbfc8088e1738f 100644 (file)
@@ -1456,6 +1456,7 @@ static bool search_arg_is_static(struct mail_search_arg *arg)
        case SEARCH_MAILBOX_GLOB:
        case SEARCH_REAL_UID:
        case SEARCH_MIMEPART:
+       case SEARCH_NIL:
                return TRUE;
        }
        return FALSE;
index 5b1c47fbaeb82d76437d9cba352ed4a419d156bc..23da429b7c2fdfbad3c0defb881502e435559918 100644 (file)
@@ -86,6 +86,8 @@ mail_search_arg_to_cmdline(string_t *dest, const struct mail_search_arg *arg)
        case SEARCH_REAL_UID:
        case SEARCH_MIMEPART:
                break;
+       case SEARCH_NIL:
+               i_unreached();
        }
        new_arg = *arg;
        new_arg.match_not = FALSE;
index e89e11940a08149c8ff1536150bf5801a54b6215..faf0a43c61cee38b240d9dd07941dd81bce356df 100644 (file)
@@ -307,6 +307,9 @@ bool mail_search_arg_to_imap(string_t *dest, const struct mail_search_arg *arg,
                        arg->value.mime_part, error_r))
                        return FALSE;
                break;
+       case SEARCH_NIL:
+               str_append(dest, "NIL ");
+               break;
        }
        return TRUE;
 }
index 2a1cace6d6912e0c2cce8112663e619ea4834b5a..e6490b542bd4212f2213315427442298d7b2e1c9 100644 (file)
@@ -535,6 +535,118 @@ mail_search_args_simplify_extract_common(struct mail_search_args *all_args,
        return TRUE;
 }
 
+static bool mail_search_args_nils_removable(enum mail_search_arg_type type) {
+       switch(type) {
+               case SEARCH_FLAGS:
+               case SEARCH_KEYWORDS:
+               case SEARCH_BEFORE:
+               case SEARCH_ON:
+               case SEARCH_SINCE:
+               case SEARCH_SMALLER:
+               case SEARCH_LARGER:
+               case SEARCH_GUID:
+               case SEARCH_MAILBOX:
+               case SEARCH_MAILBOX_GUID:
+               case SEARCH_MAILBOX_GLOB:
+               case SEARCH_MODSEQ:
+               case SEARCH_REAL_UID:
+               case SEARCH_SEQSET:
+               case SEARCH_UIDSET:
+               case SEARCH_MIMEPART:
+               case SEARCH_SAVEDATESUPPORTED:
+                       /* these want NILs to become NOT ALL */
+                       return FALSE;
+
+               case SEARCH_ALL:
+               case SEARCH_NIL:
+               case SEARCH_HEADER:
+               case SEARCH_HEADER_ADDRESS:
+               case SEARCH_HEADER_COMPRESS_LWSP:
+               case SEARCH_BODY:
+               case SEARCH_TEXT:
+                       /* these allow to remove NILs */
+                       return TRUE;
+
+               case SEARCH_INTHREAD:
+               case SEARCH_SUB:
+               case SEARCH_OR:
+                       /* these are handled in the caller as they need
+                          insight on the tree under that expression */
+                       i_unreached();
+
+               default:
+                       i_unreached();
+       }
+}
+
+static bool
+mail_search_args_handle_nils(struct mail_search_arg **argsp, bool *remove_nils_r) {
+       /* allow_remove + deny_remove + NILs count = args count */
+       int allow_remove = 0;
+       int deny_remove = 0;
+       bool changed = FALSE;
+
+       for (struct mail_search_arg *arg = *argsp; arg != NULL; arg = arg->next) {
+
+               switch(arg->type) {
+                       case SEARCH_INTHREAD:
+                       case SEARCH_SUB:
+                       case SEARCH_OR: {
+                               bool term_remove_nils;
+                               if (mail_search_args_handle_nils(
+                                       &arg->value.subargs, &term_remove_nils))
+                                       changed = TRUE;
+
+                               if (arg->value.subargs == NULL)
+                                       arg->type = SEARCH_NIL;
+                               else if (term_remove_nils)
+                                       allow_remove++;
+                               else
+                                       deny_remove++;
+                               break;
+                       }
+
+                       case SEARCH_NIL:
+                               break;
+
+                       default:
+                               if (mail_search_args_nils_removable(arg->type))
+                                       allow_remove++;
+                               else
+                                       deny_remove++;
+               }
+       }
+
+       /* The NILs can be removed if either:
+          (a) no other terms deny the removal
+          (b) or at least one other term allows the removal
+          otherwise, they are replaced with NOT ALL
+
+          [DENY, NIL]        -> [DENY, NOT ALL] -- NIL replaced with NOT ALL
+          [DENY, ALLOW, NIL] -> [DENY ALLOW]    -- NIL removed
+          [ALLOW, NIL]       -> [ALLOW]         -- NIL removed
+          [NIL]              -> []              -- NIL removed */
+       bool remove_nils = deny_remove == 0 || allow_remove > 0;
+       if (remove_nils_r != NULL) *remove_nils_r = remove_nils;
+
+       while (*argsp != NULL) {
+               bool is_nil = (*argsp)->type == SEARCH_NIL;
+               if (is_nil && remove_nils) {
+                       changed = TRUE;
+                       *argsp = (*argsp)->next;
+               } else if (is_nil) {
+                       changed = TRUE;
+                       (*argsp)->type = SEARCH_ALL;
+                       (*argsp)->match_not = TRUE;
+                       argsp = &(*argsp)->next;
+               } else {
+                       argsp = &(*argsp)->next;
+               }
+       }
+
+       return changed;
+}
+
 static bool
 mail_search_args_simplify_sub(struct mail_search_args *all_args, pool_t pool,
                              struct mail_search_arg **argsp, bool parent_and)
@@ -781,11 +893,11 @@ mail_search_args_unnest_inthreads(struct mail_search_args *args,
 
 void mail_search_args_simplify(struct mail_search_args *args)
 {
-       bool removals;
-
        args->simplified = TRUE;
 
-       removals = mail_search_args_simplify_sub(args, args->pool, &args->args, TRUE);
+       bool removals = mail_search_args_handle_nils(&args->args, NULL);
+       if (mail_search_args_simplify_sub(args, args->pool, &args->args, TRUE))
+               removals = TRUE;
        if (mail_search_args_unnest_inthreads(args, &args->args,
                                              FALSE, TRUE)) {
                /* we may have added some extra SUBs that could be dropped */
index c888b9e19513bb80aa34d83b4af59a2b4f98959e..7df05000cb463bc62d7c2f6a814a5e3639870872 100644 (file)
@@ -82,6 +82,11 @@ imap_search_all(struct mail_search_build_context *ctx)
 { 
        return mail_search_build_new(ctx, SEARCH_ALL);
 }
+static struct mail_search_arg *
+imap_search_nil(struct mail_search_build_context *ctx)
+{
+       return mail_search_build_new(ctx, SEARCH_NIL);
+}
 
 static struct mail_search_arg *
 imap_search_uid(struct mail_search_build_context *ctx)
@@ -539,6 +544,7 @@ static const struct mail_search_register_arg imap_register_args[] = {
        /* argument set operations */
        { "NOT", imap_search_not },
        { "OR", imap_search_or },
+       { "NIL", imap_search_nil },
 
        /* message sets */
        { "ALL", imap_search_all },
index fc426533ecf2f8eaab3746fc7dbb3f4eddfaeae6..e0f595d2da82ed642c5e4680c6ca731947a531a7 100644 (file)
@@ -374,6 +374,8 @@ mail_search_arg_dup_one(pool_t pool, const struct mail_search_arg *arg)
                new_arg->value.mime_part =
                        mail_search_mime_part_dup(pool, arg->value.mime_part);
                break;
+       case SEARCH_NIL:
+               i_unreached();
        }
        return new_arg;
 }
@@ -651,6 +653,8 @@ bool mail_search_arg_one_equals(const struct mail_search_arg *arg1,
                return FALSE;
 
        switch (arg1->type) {
+       case SEARCH_NIL:
+               return TRUE;
        case SEARCH_OR:
        case SEARCH_SUB:
                return mail_search_arg_equals(arg1->value.subargs,
index 8224e9ef2e5d08642dc296dc74d9ad819ded52b5..46bcbcbf256d365053b4f7bc9d8539ee486de409 100644 (file)
@@ -47,7 +47,11 @@ enum mail_search_arg_type {
        SEARCH_MAILBOX_GUID,
        SEARCH_MAILBOX_GLOB,
        SEARCH_REAL_UID,
-       SEARCH_MIMEPART
+       SEARCH_MIMEPART,
+
+       /* This term is allowed only in SEARCH_OR and SEARCH_SUB sublists.
+          When it is encountered during the simplification, it must be removed */
+       SEARCH_NIL,
 };
 
 enum mail_search_date_type {
index dcde23665b4011ffebb507b473db6a9afb69c4c8..a4803920e254f0584c07bec6a50d20c7355c4890 100644 (file)
@@ -229,6 +229,87 @@ static const struct {
        { "( OR BODY z BODY y ) ( OR BODY z BODY w ) BODY x BODY y BODY w",  "BODY x BODY y BODY w" },
 
        { "subject y", "SUBJECT y"},
+
+       /* NIL cases */
+       { "NIL", ""},
+       { "NOT NIL", ""},
+       { "( ( NIL ) )", ""},
+       { "( ( NOT NIL ) )", ""},
+
+       { "NIL NIL", "" },
+       { "( ( NIL NIL ) )", "" },
+       { "OR NIL NIL", "" },
+
+       { "OR ( NIL NIL ) OR NIL NIL", "" },
+       { "( NIL NIL ) OR NIL NIL", "" },
+
+       { "NIL LARGER 0", "NOT ALL"},
+       { "NOT NIL LARGER 0", "NOT ALL"},
+       { "NIL NOT LARGER 0", "NOT ALL"},
+       { "NOT NIL NOT LARGER 0", "NOT ALL"},
+
+       { "( NIL NIL ) TEXT uno", "TEXT uno" },
+       { "OR NIL NIL TEXT uno", "TEXT uno" },
+
+       { "TEXT foo NIL", "TEXT foo"},
+       { "NIL TEXT foo", "TEXT foo"},
+
+       { "NOT ( TEXT foo NIL )", "NOT TEXT foo"},
+       { "NOT ( NIL TEXT foo )", "NOT TEXT foo"},
+
+       { "TEXT foo NOT NIL", "TEXT foo"},
+       { "NOT NIL TEXT foo", "TEXT foo"},
+
+       { "NOT TEXT foo NIL", "NOT TEXT foo"},
+       { "NIL NOT TEXT foo", "NOT TEXT foo"},
+
+       { "OR TEXT foo NIL", "TEXT foo"},
+       { "OR NIL TEXT foo", "TEXT foo"},
+
+       { "NOT OR TEXT foo NIL", "NOT TEXT foo"},
+       { "NOT OR NIL TEXT foo", "NOT TEXT foo"},
+
+       { "OR TEXT foo NOT NIL", "TEXT foo"},
+       { "OR NOT NIL TEXT foo", "TEXT foo"},
+
+       { "OR NOT TEXT foo NIL", "NOT TEXT foo"},
+       { "OR NIL NOT TEXT foo", "NOT TEXT foo"},
+
+       // avec & and & uno & due
+       // -> {en}(avec & NIL & uno & due) | {fr}(NIL & and & uno & (du | due)
+       { "OR ( TEXT avec NIL TEXT uno TEXT due ) ( NIL TEXT and TEXT uno OR TEXT du TEXT due )",
+         "OR (TEXT avec TEXT due) (TEXT and OR TEXT du TEXT due) TEXT uno" },
+
+       // avec | and | uno | due
+       // -> {en}(avec | NIL | uno | due) | {fr}(NIL | and | uno | (du | due)
+       { "OR ( OR TEXT avec OR NIL OR TEXT uno TEXT due ) ( OR NIL OR TEXT and OR TEXT uno OR TEXT du TEXT due )",
+         "OR TEXT avec OR TEXT uno OR TEXT due OR TEXT and TEXT du" },
+
+       // (avec | and) & (uno | due)
+       // -> {en}((avec | NIL) & (uno | due)) | {fr}((NIL | and) & (uno | (du | due)))
+       { "OR ( OR TEXT avec NIL OR TEXT uno TEXT due ) ( OR NIL TEXT and OR TEXT uno ( OR TEXT du TEXT due ) )",
+         "OR (TEXT avec OR TEXT uno TEXT due) (TEXT and OR TEXT uno OR TEXT du TEXT due)" },
+
+       // (avec | uno) & (and | due)
+       // -> {en}((avec | uno) & (NIL | due)) | {fr}((NIL | uno) & (and | (du | due)))
+       { "OR ( OR TEXT avec TEXT uno OR NIL TEXT due ) ( OR NIL TEXT uno OR TEXT and OR TEXT du TEXT due )",
+         "OR (OR TEXT avec TEXT uno TEXT due) (TEXT uno OR TEXT and OR TEXT du TEXT due)"},
+
+       // (uno | due) & ( tre | NIL)
+       { "OR TEXT uno TEXT due OR TEXT tre NIL",
+         "OR TEXT uno TEXT due TEXT tre" },
+
+       // (uno & due) | ( tre & NIL)
+       { "OR ( TEXT uno TEXT due ) ( TEXT tre NIL )",
+         "OR (TEXT uno TEXT due) TEXT tre" },
+
+       // (uno | due) & ( uno | NIL)
+       { "OR TEXT uno TEXT due OR TEXT uno NIL",
+         "TEXT uno" },
+
+       // (uno & due) | ( uno & NIL)
+       { "OR ( TEXT uno TEXT due ) ( TEXT uno NIL )",
+         "TEXT uno" },
 };
 
 static struct mail_search_args *