]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imap: Simplify SEARCH MIN and MAX result handling
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 16 Mar 2020 09:50:38 +0000 (11:50 +0200)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Fri, 20 Mar 2020 08:03:57 +0000 (08:03 +0000)
src/imap/imap-search.c
src/imap/imap-search.h

index 65f6eae0783182dae66afdc6e965f69323aa2658..9c9737cd4d0e9685f937ec5398a6c5d90970da71 100644 (file)
@@ -308,8 +308,6 @@ imap_search_send_relevancy(struct imap_search_context *ctx, string_t *dest)
 static void imap_search_send_result(struct imap_search_context *ctx)
 {
        struct client *client = ctx->cmd->client;
-       const struct seq_range *range;
-       unsigned int count;
        string_t *str;
 
        if ((ctx->return_options & SEARCH_RETURN_ESEARCH) == 0) {
@@ -332,16 +330,18 @@ static void imap_search_send_result(struct imap_search_context *ctx)
        if (ctx->cmd->uid)
                str_append(str, " UID");
 
-       range = array_get(&ctx->result, &count);
-       if (count > 0) {
-               if ((ctx->return_options & SEARCH_RETURN_MIN) != 0)
-                       str_printfa(str, " MIN %u", range[0].seq1);
-               if ((ctx->return_options & SEARCH_RETURN_MAX) != 0)
-                       str_printfa(str, " MAX %u", range[count-1].seq2);
-               if ((ctx->return_options & SEARCH_RETURN_ALL) != 0) {
-                       str_append(str, " ALL ");
-                       imap_write_seq_range(str, &ctx->result);
-               }
+       if ((ctx->return_options & SEARCH_RETURN_MIN) != 0 && ctx->min_id != 0)
+               str_printfa(str, " MIN %u", ctx->min_id);
+       if ((ctx->return_options & SEARCH_RETURN_MAX) != 0 &&
+           ctx->max_seq != 0) {
+               uint32_t id = ctx->cmd->uid ? ctx->max_uid : ctx->max_seq;
+               str_printfa(str, " MAX %u", id);
+       }
+
+       if ((ctx->return_options & SEARCH_RETURN_ALL) != 0 &&
+           array_count(&ctx->result) > 0) {
+               str_append(str, " ALL ");
+               imap_write_seq_range(str, &ctx->result);
        }
        if ((ctx->return_options & SEARCH_RETURN_RELEVANCY) != 0) {
                str_append(str, " RELEVANCY (");
@@ -368,6 +368,12 @@ search_update_mail(struct imap_search_context *ctx, struct mail *mail)
 {
        uint64_t modseq;
 
+       if (ctx->max_update_seq == mail->seq) {
+               /* MIN already handled this mail */
+               return;
+       }
+       ctx->max_update_seq = mail->seq;
+
        if ((ctx->return_options & SEARCH_RETURN_MODSEQ) != 0) {
                modseq = mail_get_modseq(mail);
                if (ctx->highest_seen_modseq < modseq)
@@ -415,83 +421,53 @@ static bool cmd_search_more(struct client_command_context *cmd)
        enum search_return_options opts = ctx->return_options;
        struct mail *mail;
        enum mailbox_sync_flags sync_flags;
-       const struct seq_range *range;
-       unsigned int count;
-       uint32_t id, id_min, id_max;
+       uint32_t id;
        const char *ok_reply;
-       bool tryagain, minmax, lost_data;
+       bool tryagain, lost_data;
 
        if (cmd->cancel) {
                (void)imap_search_deinit(ctx);
                return TRUE;
        }
 
-       range = array_get(&ctx->result, &count);
-       if (count == 0) {
-               id_min = 0;
-               id_max = 0;
-       } else {
-               id_min = range[0].seq1;
-               id_max = range[count-1].seq2;
-       }
-
-       minmax = (opts & (SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) != 0 &&
-               (opts & ~(SEARCH_RETURN_NORESULTS |
-                         SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) == 0;
        while (mailbox_search_next_nonblock(ctx->search_ctx,
                                            &mail, &tryagain)) {
                id = cmd->uid ? mail->uid : mail->seq;
                ctx->result_count++;
 
-               if (minmax) {
-                       /* we only care about min/max */
-                       if (id_min == 0 && (opts & SEARCH_RETURN_MIN) != 0)
-                               id_min = id;
-                       if ((opts & SEARCH_RETURN_MAX) != 0)
-                               id_max = id;
-                       if (id == id_min || id == id_max) {
-                               /* return option updates are delayed until
-                                  we know the actual min/max values */
-                               search_add_result_id(ctx, id);
-                       }
-                       continue;
+               ctx->max_seq = mail->seq;
+               ctx->max_uid = mail->uid;
+               if (HAS_ANY_BITS(opts, SEARCH_RETURN_MIN) && ctx->min_id == 0) {
+                       /* MIN not set yet */
+                       ctx->min_id = id;
+                       search_update_mail(ctx, mail);
                }
-
-               search_update_mail(ctx, mail);
-               if ((opts & ~(SEARCH_RETURN_NORESULTS |
-                             SEARCH_RETURN_COUNT)) == 0) {
-                       /* we only want to count (and get modseqs) */
+               if (HAS_ANY_BITS(opts, SEARCH_RETURN_ALL))
+                       search_add_result_id(ctx, id);
+               else if (HAS_ANY_BITS(opts, SEARCH_RETURN_COUNT)) {
+                       /* with COUNT don't add it to results, but handle
+                          SAVE and MODSEQ */
+               } else if (HAS_ANY_BITS(opts, SEARCH_RETURN_MIN |
+                                       SEARCH_RETURN_MAX)) {
+                       /* MIN and/or MAX only requested, but we don't know if
+                          this is MAX until the search is finished. */
                        continue;
+               } else if (HAS_ANY_BITS(opts, SEARCH_RETURN_SAVE)) {
+                       /* Only SAVE used */
                }
-               search_add_result_id(ctx, id);
+               search_update_mail(ctx, mail);
        }
        if (tryagain)
                return FALSE;
 
-       if (minmax && array_count(&ctx->result) > 0 &&
-           (opts & (SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE)) != 0) {
-               /* handle MIN/MAX modseq/save updates */
+       if ((opts & SEARCH_RETURN_MAX) != 0 && ctx->max_seq != 0 &&
+           ctx->max_update_seq != ctx->max_seq &&
+           HAS_ANY_BITS(opts, SEARCH_RETURN_MODSEQ |
+                        SEARCH_RETURN_SAVE)) {
+               /* finish handling MAX */
                mail = mail_alloc(ctx->trans, 0, NULL);
-               if ((opts & SEARCH_RETURN_MIN) != 0) {
-                       i_assert(id_min != 0);
-                       if (cmd->uid) {
-                               if (!mail_set_uid(mail, id_min))
-                                       i_unreached();
-                       } else {
-                               mail_set_seq(mail, id_min);
-                       }
-                       search_update_mail(ctx, mail);
-               }
-               if ((opts & SEARCH_RETURN_MAX) != 0) {
-                       i_assert(id_max != 0);
-                       if (cmd->uid) {
-                               if (!mail_set_uid(mail, id_max))
-                                       i_unreached();
-                       } else {
-                               mail_set_seq(mail, id_max);
-                       }
-                       search_update_mail(ctx, mail);
-               }
+               mail_set_seq(mail, ctx->max_seq);
+               search_update_mail(ctx, mail);
                mail_free(&mail);
        }
 
index 7e8951336080ae4e3e398485f6efe8872ec96648..f66537501c2b3b9f76631b1939d48a7063bc1a7a 100644 (file)
@@ -37,6 +37,7 @@ struct imap_search_context {
        struct timeout *to;
        ARRAY_TYPE(seq_range) result;
        unsigned int result_count;
+       uint32_t min_id, max_update_seq, max_seq, max_uid;
 
        ARRAY(float) relevancy_scores;
        float min_relevancy, max_relevancy;