]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: mail_search_args_simplify() - deduplicate flags
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 13 Apr 2017 12:13:19 +0000 (15:13 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 20 Apr 2017 18:56:17 +0000 (21:56 +0300)
This needs to be done in a bit more complicated way because multiple
SEARCH_FLAGS parameters are wanted to be merged together using a single
shared value.flags. Move this merging last after all the deduplication is
done.

src/lib-storage/mail-search-args-simplify.c
src/lib-storage/test-mail-search-args-simplify.c

index e641f19099b5fe5c94efcdb0570bab84022fc28a..7c7a24e08cf90125a2c9cf48331dfb028d32f043 100644 (file)
@@ -10,6 +10,7 @@ struct mail_search_simplify_prev_arg {
                enum mail_search_arg_type type;
                enum mail_search_arg_flag search_flags;
                enum mail_search_date_type date_type;
+               enum mail_flags mail_flags;
                bool match_not;
                bool fuzzy;
        } bin_mask;
@@ -108,23 +109,10 @@ static bool mail_search_args_merge_flags(struct mail_search_simplify_ctx *ctx,
                                         struct mail_search_arg *args)
 {
        struct mail_search_simplify_prev_arg mask;
-       struct mail_search_arg **prev_argp;
-
-       if (!((!args->match_not && ctx->parent_and) ||
-             (args->match_not && !ctx->parent_and)))
-               return FALSE;
 
        mail_search_arg_get_base_mask(args, &mask);
-       mask.bin_mask.match_not = args->match_not;
-       prev_argp = mail_search_args_simplify_get_prev_argp(ctx, &mask);
-
-       if (*prev_argp == NULL) {
-               *prev_argp = args;
-               return FALSE;
-       } else {
-               (*prev_argp)->value.flags |= args->value.flags;
-               return TRUE;
-       }
+       mask.bin_mask.mail_flags = args->value.flags;
+       return mail_search_args_merge_mask(ctx, args, &mask);
 }
 
 static bool
@@ -663,6 +651,42 @@ mail_search_args_simplify_sub(struct mailbox *box, pool_t pool,
        return ctx.removals;
 }
 
+static bool
+mail_search_args_simplify_merge_flags(struct mail_search_arg **argsp,
+                                     bool parent_and)
+{
+       struct mail_search_arg *prev_flags = NULL;
+       bool removals = FALSE;
+
+       while (*argsp != NULL) {
+               struct mail_search_arg *args = *argsp;
+
+               if (args->type == SEARCH_SUB ||
+                   args->type == SEARCH_OR ||
+                   args->type == SEARCH_INTHREAD) {
+                       if (mail_search_args_simplify_merge_flags(&args->value.subargs,
+                                                                 args->type != SEARCH_OR))
+                               removals = TRUE;
+               } else if (args->type != SEARCH_FLAGS) {
+                       /* ignore non-flags */
+               } else if (!((!args->match_not && parent_and) ||
+                          (args->match_not && !parent_and))) {
+                       /* can't merge these flags args */
+               } else if (prev_flags == NULL) {
+                       /* first flags arg */
+                       prev_flags = args;
+               } else {
+                       /* merge to previous arg */
+                       prev_flags->value.flags |= args->value.flags;
+                       *argsp = args->next;
+                       removals = TRUE;
+                       continue;
+               }
+               argsp = &args->next;
+       }
+       return removals;
+}
+
 static bool
 mail_search_args_unnest_inthreads(struct mail_search_args *args,
                                  struct mail_search_arg **argp,
@@ -742,13 +766,18 @@ void mail_search_args_simplify(struct mail_search_args *args)
                if (mail_search_args_simplify_sub(args->box, args->pool, &args->args, TRUE))
                        removals = TRUE;
        }
-       for (;;) {
+       do {
                if (mail_search_args_simplify_drop_redundant_args(&args->args, TRUE))
                        removals = TRUE;
                if (mail_search_args_simplify_extract_common(&args->args, args->pool, TRUE))
                        removals = TRUE;
-               if (!removals)
-                       break;
-               removals = mail_search_args_simplify_sub(args->box, args->pool, &args->args, TRUE);
-       }
+               if (removals)
+                       removals = mail_search_args_simplify_sub(args->box, args->pool, &args->args, TRUE);
+               /* do the flag merging into a single arg only at the end.
+                  up until then they're treated as any other search args,
+                  which simplifies their handling. after the flags merging is
+                  done, further simplifications are still possible. */
+               if (mail_search_args_simplify_merge_flags(&args->args, TRUE))
+                       removals = TRUE;
+       } while (removals);
 }
index 78001ce9c722e3e8effd4b2cc8b92015caf159df..d491a9445d8772db7f239adb15df240ed1a27387 100644 (file)
@@ -51,6 +51,14 @@ struct {
        { "OR NOT ANSWERED NOT SEEN", "NOT (ANSWERED SEEN)" },
        { "OR NOT ANSWERED OR NOT SEEN TEXT foo", "(OR NOT (ANSWERED SEEN) TEXT foo)" },
 
+       { "ANSWERED ANSWERED", "ANSWERED" },
+       { "ANSWERED NOT ANSWERED", "NOT ALL" },
+       { "ANSWERED ANSWERED NOT ANSWERED", "NOT ALL" },
+       { "ANSWERED NOT ANSWERED ANSWERED NOT ANSWERED", "NOT ALL" },
+       { "NOT ANSWERED NOT ANSWERED", "NOT ANSWERED" },
+       { "NOT SEEN NOT ANSWERED NOT ANSWERED", "NOT SEEN NOT ANSWERED" },
+       { "OR NOT SEEN OR NOT ANSWERED NOT ANSWERED", "NOT (ANSWERED SEEN)" },
+
        { "KEYWORD foo", "KEYWORD foo" },
        { "KEYWORD foo KEYWORD bar", "KEYWORD foo KEYWORD bar" },
        { "NOT KEYWORD foo", "NOT KEYWORD foo" },