]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Some optimizations to messageset handling in search.
authorTimo Sirainen <tss@iki.fi>
Sun, 26 Oct 2003 19:41:09 +0000 (21:41 +0200)
committerTimo Sirainen <tss@iki.fi>
Sun, 26 Oct 2003 19:41:09 +0000 (21:41 +0200)
--HG--
branch : HEAD

src/lib-storage/index/index-messageset.c
src/lib-storage/index/index-messageset.h
src/lib-storage/index/index-search.c
src/lib-storage/mail-search.c
src/lib-storage/mail-search.h

index 9232735a718a6bd4041dcd144f9a5f5ce1010c7f..0c69beaa14860ec860871744f5c8a74bd07d3659 100644 (file)
@@ -17,6 +17,7 @@ struct messageset_context {
        struct messageset_mail mail;
        unsigned int messages_count;
        unsigned int num1, num2;
+       unsigned int min_uid, max_uid;
 
        const char *messageset, *p;
        int uidset, skip_expunged;
@@ -44,6 +45,9 @@ index_messageset_init(struct index_mailbox *ibox,
        ctx->uidset = uidset;
        ctx->skip_expunged = skip_expunged;
 
+       ctx->min_uid = 1;
+       ctx->max_uid = (unsigned int)-1;
+
        /* Reset index errors, we rely on it to check for failures */
        index_reset_error(ctx->index);
 
@@ -67,6 +71,13 @@ index_messageset_init_range(struct index_mailbox *ibox,
        return ctx;
 }
 
+void index_messageset_limit_range(struct messageset_context *ctx,
+                                 unsigned int min_uid, unsigned int max_uid)
+{
+       ctx->min_uid = min_uid;
+       ctx->max_uid = max_uid;
+}
+
 int index_messageset_deinit(struct messageset_context *ctx)
 {
        int ret = ctx->ret;
@@ -220,6 +231,14 @@ static int uidset_init(struct messageset_context *ctx)
                        ctx->num2 = temp;
                }
        }
+       i_assert(ctx->num1 <= ctx->num2);
+
+       if (ctx->num1 < ctx->min_uid)
+               ctx->num1 = ctx->min_uid;
+       if (ctx->num2 > ctx->max_uid)
+               ctx->num2 = ctx->max_uid;
+       if (ctx->num1 > ctx->num2)
+               return 1;
 
        /* get list of expunged messages in our range. */
        ctx->expunges = mail_modifylog_uid_get_expunges(ctx->index->modifylog,
@@ -269,13 +288,24 @@ static int seqset_init(struct messageset_context *ctx)
        /* get the first non-expunged message. note that if all messages
           were expunged in the range, this points outside wanted range. */
        ctx->mail.idx_seq = ctx->num1 - expunges_before;
+       ctx->mail.client_seq = ctx->num1;
        ctx->mail.rec = ctx->index->lookup(ctx->index, ctx->mail.idx_seq);
+       while (ctx->mail.rec != NULL && ctx->mail.rec->uid < ctx->min_uid) {
+               ctx->mail.idx_seq++;
+               ctx->mail.client_seq++;
+               ctx->mail.rec = ctx->index->next(ctx->index, ctx->mail.rec);
+       }
+
+       if (ctx->mail.rec != NULL && ctx->mail.rec->uid > ctx->max_uid) {
+               ctx->mail.rec = NULL;
+               return 1;
+       }
+
        if (ctx->mail.rec == NULL) {
                return ctx->index->get_last_error(ctx->index) ==
                        MAIL_INDEX_ERROR_NONE ? 1 : -1;
        }
 
-       ctx->mail.client_seq = ctx->num1;
        return 0;
 }
 
index 8ee3c1eb3dd893786995a2232b9f9617423c0d73..3c3ee894f48913d0aba36779ad76bd184f2a6068 100644 (file)
@@ -19,6 +19,9 @@ struct messageset_context *
 index_messageset_init_range(struct index_mailbox *ibox,
                            unsigned int num1, unsigned int num2, int uidset);
 
+void index_messageset_limit_range(struct messageset_context *ctx,
+                                 unsigned int min_uid, unsigned int max_uid);
+
 /* Returns 1 if all were found, 0 if some messages were expunged,
    -1 if internal error occured or -2 if messageset was invalid. */
 int index_messageset_deinit(struct messageset_context *ctx);
index a9a012e4fa069085090e7a11c83a964adb7f73ee..d93f732753e7d499b33cc1224b009d02865cd443 100644 (file)
@@ -611,21 +611,32 @@ static int seq_update(const char *set, unsigned int *first_seq,
        return TRUE;
 }
 
-static int search_get_sequid(struct index_mailbox *ibox,
-                            const struct mail_search_arg *args,
-                            unsigned int *first_seq, unsigned int *last_seq,
-                            unsigned int *first_uid, unsigned int *last_uid)
+struct search_msgset_context {
+       struct index_mailbox *ibox;
+
+       unsigned int first_seq, last_seq;
+       unsigned int first_uid, last_uid;
+
+       struct mail_search_arg *msgset_arg;
+       unsigned int msgset_arg_count;
+};
+
+static int search_parse_msgset_args(struct search_msgset_context *ctx,
+                                   struct mail_search_arg *args)
 {
+       struct index_mailbox *ibox = ctx->ibox;
+
        for (; args != NULL; args = args->next) {
                /* FIXME: we don't check if OR condition can limit the range.
                   It's a bit tricky and unlikely to affect performance much. */
                if (args->type == SEARCH_SUB) {
-                       if (!search_get_sequid(ibox, args->value.subargs,
-                                              first_seq, last_seq,
-                                              first_uid, last_uid))
+                       if (!search_parse_msgset_args(ctx, args->value.subargs))
                                return FALSE;
                } else if (args->type == SEARCH_SET) {
-                       if (!seq_update(args->value.str, first_seq, last_seq,
+                       ctx->msgset_arg = args;
+                       ctx->msgset_arg_count++;
+                       if (!seq_update(args->value.str,
+                                       &ctx->first_seq, &ctx->last_seq,
                                        ibox->synced_messages_count)) {
                                mail_storage_set_syntax_error(ibox->box.storage,
                                        "Invalid messageset: %s",
@@ -633,7 +644,10 @@ static int search_get_sequid(struct index_mailbox *ibox,
                                return FALSE;
                        }
                } else if (args->type == SEARCH_UID) {
-                       if (!seq_update(args->value.str, first_uid, last_uid,
+                       ctx->msgset_arg = args;
+                       ctx->msgset_arg_count++;
+                       if (!seq_update(args->value.str,
+                                       &ctx->first_uid, &ctx->last_uid,
                                        ibox->index->header->next_uid-1)) {
                                mail_storage_set_syntax_error(ibox->box.storage,
                                        "Invalid messageset: %s",
@@ -642,8 +656,9 @@ static int search_get_sequid(struct index_mailbox *ibox,
                        }
                } else if (args->type == SEARCH_ALL) {
                        /* go through everything */
-                       *first_seq = 1;
-                       *last_seq = ibox->synced_messages_count;
+                       ctx->first_seq = 1;
+                       ctx->last_seq = ibox->synced_messages_count;
+                       ctx->msgset_arg_count++;
                        return TRUE;
                }
        }
@@ -652,7 +667,7 @@ static int search_get_sequid(struct index_mailbox *ibox,
 }
 
 static int search_limit_by_flags(struct index_mailbox *ibox,
-                                const struct mail_search_arg *args,
+                                struct mail_search_arg *args,
                                 unsigned int *first_uid,
                                 unsigned int *last_uid)
 {
@@ -666,15 +681,19 @@ static int search_limit_by_flags(struct index_mailbox *ibox,
                        if (!args->not && hdr->seen_messages_count == 0)
                                return FALSE;
 
-                       /* UNSEEN with all seen? */
-                       if (args->not &&
-                           hdr->seen_messages_count == hdr->messages_count)
-                               return FALSE;
+                       if (hdr->seen_messages_count == hdr->messages_count) {
+                               /* UNSEEN with all seen? */
+                               if (args->not)
+                                       return FALSE;
 
-                       /* UNSEEN with lowwater limiting */
-                       uid = hdr->first_unseen_uid_lowwater;
-                       if (args->not && *first_uid < uid)
-                               *first_uid = uid;
+                               /* SEEN with all seen */
+                               args->match_always = TRUE;
+                       } else {
+                               /* UNSEEN with lowwater limiting */
+                               uid = hdr->first_unseen_uid_lowwater;
+                               if (args->not && *first_uid < uid)
+                                       *first_uid = uid;
+                       }
                }
 
                if (args->type == SEARCH_DELETED) {
@@ -682,15 +701,20 @@ static int search_limit_by_flags(struct index_mailbox *ibox,
                        if (!args->not && hdr->deleted_messages_count == 0)
                                return FALSE;
 
-                       /* UNDELETED with all deleted? */
-                       if (args->not &&
-                           hdr->deleted_messages_count == hdr->messages_count)
-                               return FALSE;
+                       if (hdr->deleted_messages_count ==
+                           hdr->messages_count) {
+                               /* UNDELETED with all deleted? */
+                               if (args->not)
+                                       return FALSE;
 
-                       /* DELETED with lowwater limiting */
-                       uid = hdr->first_deleted_uid_lowwater;
-                       if (!args->not && *first_uid < uid)
-                               *first_uid = uid;
+                               /* DELETED with all deleted */
+                               args->match_always = TRUE;
+                       } else {
+                               /* DELETED with lowwater limiting */
+                               uid = hdr->first_deleted_uid_lowwater;
+                               if (!args->not && *first_uid < uid)
+                                       *first_uid = uid;
+                       }
                }
 
                if (args->type == SEARCH_RECENT) {
@@ -728,53 +752,67 @@ static int client_seq_to_uid(struct index_mailbox *ibox,
        return TRUE;
 }
 
-static int search_get_uid_range(struct index_mailbox *ibox,
-                               const struct mail_search_arg *args,
-                               unsigned int *first_uid, unsigned int *last_uid)
+static int search_get_msgset(struct index_mailbox *ibox,
+                            struct mail_search_arg *args,
+                            struct messageset_context **msgset_r)
 {
-       unsigned int first_seq, last_seq, uid;
+        struct search_msgset_context ctx;
+       unsigned int uid;
 
-       *first_uid = *last_uid = 0;
-       first_seq = last_seq = 0;
+       memset(&ctx, 0, sizeof(ctx));
+       ctx.ibox = ibox;
 
-       if (!search_get_sequid(ibox, args, &first_seq, &last_seq,
-                              first_uid, last_uid))
+       if (!search_parse_msgset_args(&ctx, args))
                return -1;
 
        /* seq_update() should make sure that these can't happen */
-       i_assert(first_seq <= last_seq);
-       i_assert(*first_uid <= *last_uid);
+       i_assert(ctx.first_seq <= ctx.last_seq);
+       i_assert(ctx.first_uid <= ctx.last_uid);
 
-       if (first_seq > 1) {
-               if (!client_seq_to_uid(ibox, first_seq, &uid))
+       if (ctx.first_seq > 1) {
+               if (!client_seq_to_uid(ibox, ctx.first_seq, &uid))
                        return -1;
                if (uid == 0)
                        return 0;
 
-               if (*first_uid == 0 || uid < *first_uid)
-                       *first_uid = uid;
+               if (ctx.first_uid == 0 || uid < ctx.first_uid)
+                       ctx.first_uid = uid;
        }
 
-       if (last_seq > 1 && last_seq != ibox->synced_messages_count) {
-               if (!client_seq_to_uid(ibox, last_seq, &uid))
+       if (ctx.last_seq > 1 && ctx.last_seq != ibox->synced_messages_count) {
+               if (!client_seq_to_uid(ibox, ctx.last_seq, &uid))
                        return -1;
                if (uid == 0)
                        return 0;
 
-               if (*last_uid == 0 || uid > *last_uid)
-                       *last_uid = uid;
+               if (ctx.last_uid == 0 || uid > ctx.last_uid)
+                       ctx.last_uid = uid;
        }
 
-       if (*first_uid == 0)
-               *first_uid = 1;
-       if (*last_uid == 0 || last_seq == ibox->synced_messages_count)
-               *last_uid = ibox->index->header->next_uid-1;
+       if (ctx.first_uid == 0)
+               ctx.first_uid = 1;
+       if (ctx.last_uid == 0 || ctx.last_seq == ibox->synced_messages_count)
+               ctx.last_uid = ibox->index->header->next_uid-1;
 
        /* UNSEEN and DELETED in root search level may limit the range */
-       if (!search_limit_by_flags(ibox, args, first_uid, last_uid))
+       if (!search_limit_by_flags(ibox, args, &ctx.first_uid, &ctx.last_uid))
                return 0;
 
-       i_assert(*first_uid <= *last_uid);
+       i_assert(ctx.first_uid <= ctx.last_uid);
+
+       if (ctx.msgset_arg != NULL && ctx.msgset_arg_count == 1) {
+               /* one messageset argument, we can use it */
+               *msgset_r = index_messageset_init(ibox,
+                               ctx.msgset_arg->value.str,
+                               ctx.msgset_arg->type == SEARCH_UID, TRUE);
+               /* we might be able to limit it some more */
+               index_messageset_limit_range(*msgset_r,
+                                            ctx.first_uid, ctx.last_uid);
+               ctx.msgset_arg->match_always = TRUE;
+       } else {
+               *msgset_r = index_messageset_init_range(ibox, ctx.first_uid,
+                                                       ctx.last_uid, TRUE);
+       }
        return 1;
 }
 
@@ -795,7 +833,6 @@ index_storage_search_init(struct mailbox *box, const char *charset,
 {
        struct index_mailbox *ibox = (struct index_mailbox *) box;
        struct mail_search_context *ctx;
-       unsigned int first_uid, last_uid;
 
        if (sort_program != NULL && *sort_program != MAIL_SORT_END) {
                i_error("BUG: index_storage_search_init(): "
@@ -817,8 +854,10 @@ index_storage_search_init(struct mailbox *box, const char *charset,
        if (ibox->synced_messages_count == 0)
                return ctx;
 
+       mail_search_args_reset(ctx->args, TRUE);
+
        /* see if we can limit the records we look at */
-       switch (search_get_uid_range(ibox, args, &first_uid, &last_uid)) {
+       switch (search_get_msgset(ibox, args, &ctx->msgset_ctx)) {
        case -1:
                /* error */
                ctx->failed = TRUE;
@@ -828,8 +867,6 @@ index_storage_search_init(struct mailbox *box, const char *charset,
                return ctx;
        }
 
-       ctx->msgset_ctx =
-               index_messageset_init_range(ibox, first_uid, last_uid, TRUE);
        return ctx;
 }
 
@@ -870,7 +907,7 @@ static int search_match_next(struct mail_search_context *ctx)
        int ret;
 
        /* check the index matches first */
-       mail_search_args_reset(ctx->args);
+       mail_search_args_reset(ctx->args, FALSE);
        ret = mail_search_args_foreach(ctx->args, search_index_arg, ctx);
        if (ret >= 0)
                return ret > 0;
index 86d56d413f64c7b22556c02225223c2aecbd5edb..cd520f60dc2a6247ed33189c8a8021ae8960ed03 100644 (file)
@@ -4,12 +4,22 @@
 #include "buffer.h"
 #include "mail-search.h"
 
-void mail_search_args_reset(struct mail_search_arg *args)
+void mail_search_args_reset(struct mail_search_arg *args, int full_reset)
 {
        while (args != NULL) {
                if (args->type == SEARCH_OR || args->type == SEARCH_SUB)
-                       mail_search_args_reset(args->value.subargs);
-               args->result = -1;
+                       mail_search_args_reset(args->value.subargs, full_reset);
+
+               if (!args->match_always)
+                       args->result = -1;
+               else {
+                       if (!full_reset)
+                               args->result = 1;
+                       else {
+                               args->match_always = FALSE;
+                               args->result = -1;
+                       }
+               }
 
                args = args->next;
        }
index 90a2c99baa04a94b3581cb8d288795fae598b1e1..851f30a384697216db6678cda5744201d935dded 100644 (file)
@@ -52,6 +52,7 @@ struct mail_search_arg {
         void *context;
        const char *hdr_field_name; /* for SEARCH_HEADER* */
        unsigned int not:1;
+       unsigned int match_always:1; /* result = 1 always */
 
        int result; /* -1 = unknown, 0 = unmatched, 1 = matched */
 };
@@ -65,8 +66,9 @@ struct mail_search_arg {
 typedef void (*mail_search_foreach_callback_t)(struct mail_search_arg *arg,
                                               void *context);
 
-/* Reset the results in search arguments */
-void mail_search_args_reset(struct mail_search_arg *args);
+/* Reset the results in search arguments. match_always is reset only if
+   full_reset is TRUE. */
+void mail_search_args_reset(struct mail_search_arg *args, int full_reset);
 
 /* goes through arguments in list that don't have a result yet.
    Returns 1 = search matched, 0 = search unmatched, -1 = don't know yet */