]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added non-optimized support for SEARCH INTHREAD.
authorTimo Sirainen <tss@iki.fi>
Fri, 20 Jun 2008 02:37:30 +0000 (05:37 +0300)
committerTimo Sirainen <tss@iki.fi>
Fri, 20 Jun 2008 02:37:30 +0000 (05:37 +0300)
--HG--
branch : HEAD

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

index 416807f4fe463dff9b36539f373e2d6125e590cc..03e34f8793c7c02a9eaa37caec4658036c9bede8 100644 (file)
@@ -34,6 +34,7 @@ struct index_search_context {
        uint32_t seq1, seq2;
        struct mail *mail;
        struct index_mail *imail;
+       struct mail_thread_context *thread_ctx;
 
        const char *error;
 
@@ -77,6 +78,7 @@ static void search_init_arg(struct mail_search_arg *arg,
                ctx->have_seqsets = TRUE;
                break;
        case SEARCH_UIDSET:
+       case SEARCH_INTHREAD:
        case SEARCH_FLAGS:
        case SEARCH_KEYWORDS:
        case SEARCH_MODSEQ:
@@ -140,6 +142,7 @@ static int search_arg_match_index(struct index_search_context *ctx,
 
        switch (arg->type) {
        case SEARCH_UIDSET:
+       case SEARCH_INTHREAD:
                return seq_range_exists(&arg->value.seqset, rec->uid);
        case SEARCH_FLAGS:
                /* recent flag shouldn't be set, but indexes from v1.0.x
@@ -858,6 +861,96 @@ static void search_get_seqset(struct index_search_context *ctx,
        }
 }
 
+static int search_build_subthread(struct mail_thread_iterate_context *iter,
+                                 ARRAY_TYPE(seq_range) *uids)
+{
+       struct mail_thread_iterate_context *child_iter;
+       const struct mail_thread_child_node *node;
+       int ret = 0;
+
+       while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) {
+               if (child_iter != NULL) {
+                       if (search_build_subthread(child_iter, uids) < 0)
+                               ret = -1;
+               }
+               seq_range_array_add(uids, 0, node->uid);
+       }
+       if (mail_thread_iterate_deinit(&iter) < 0)
+               ret = -1;
+       return ret;
+}
+
+static int search_build_inthread_result(struct index_search_context *ctx,
+                                       struct mail_search_arg *arg)
+{
+       struct mail_thread_iterate_context *iter, *child_iter;
+       const struct mail_thread_child_node *node;
+       const ARRAY_TYPE(seq_range) *search_uids;
+       ARRAY_TYPE(seq_range) thread_uids;
+       int ret;
+
+       p_array_init(&arg->value.seqset, ctx->mail_ctx.args->pool, 64);
+       if (mailbox_search_result_build(ctx->mail_ctx.transaction,
+                                       arg->value.search_args,
+                                       MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
+                                       MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC,
+                                       &arg->value.search_result) < 0)
+               return -1;
+       if (ctx->thread_ctx == NULL) {
+               /* failed earlier */
+               return -1;
+       }
+
+       search_uids = mailbox_search_result_get(arg->value.search_result);
+       if (array_count(search_uids) == 0) {
+               /* search found nothing - no threads can match */
+               return 0;
+       }
+
+       t_array_init(&thread_uids, 128);
+       iter = mail_thread_iterate_init(ctx->thread_ctx,
+                                       arg->value.thread_type, FALSE);
+       while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) {
+               seq_range_array_add(&thread_uids, 0, node->uid);
+               if (child_iter != NULL) {
+                       if (search_build_subthread(child_iter,
+                                                  &thread_uids) < 0)
+                               ret = -1;
+               }
+               if (seq_range_array_have_common(&thread_uids, search_uids)) {
+                       /* yes, we want this thread */
+                       seq_range_array_merge(&arg->value.seqset, &thread_uids);
+               }
+               array_clear(&thread_uids);
+       }
+       if (mail_thread_iterate_deinit(&iter) < 0)
+               ret = -1;
+       return ret;
+}
+
+static int search_build_inthreads(struct index_search_context *ctx,
+                                 struct mail_search_arg *arg)
+{
+       int ret = 0;
+
+       for (; arg != NULL; arg = arg->next) {
+               switch (arg->type) {
+               case SEARCH_OR:
+               case SEARCH_SUB:
+                       if (search_build_inthreads(ctx, arg->value.subargs) < 0)
+                               ret = -1;
+                       break;
+               case SEARCH_INTHREAD:
+                       if (search_build_inthread_result(ctx, arg) < 0)
+                               ret = -1;
+                       break;
+               default:
+                       break;
+               }
+       }
+       return ret;
+}
+
 struct mail_search_context *
 index_storage_search_init(struct mailbox_transaction_context *_t,
                          struct mail_search_args *args,
@@ -879,6 +972,13 @@ index_storage_search_init(struct mailbox_transaction_context *_t,
                     sizeof(void *), 5);
 
        mail_search_args_reset(ctx->mail_ctx.args->args, TRUE);
+       if (args->have_inthreads) {
+               if (mail_thread_init(_t->box, FALSE, NULL,
+                                    &ctx->thread_ctx) < 0)
+                       ctx->failed = TRUE;
+               if (search_build_inthreads(ctx, args->args) < 0)
+                       ctx->failed = TRUE;
+       }
 
        search_get_seqset(ctx, args->args);
        (void)mail_search_args_foreach(args->args, search_init_arg, ctx);
@@ -917,6 +1017,8 @@ int index_storage_search_deinit(struct mail_search_context *_ctx)
 
        if (ctx->mail_ctx.sort_program != NULL)
                index_sort_program_deinit(&ctx->mail_ctx.sort_program);
+       if (ctx->thread_ctx != NULL)
+               mail_thread_deinit(&ctx->thread_ctx);
        array_free(&ctx->mail_ctx.results);
        array_free(&ctx->mail_ctx.module_contexts);
        i_free(ctx);
@@ -1001,6 +1103,7 @@ static bool search_arg_is_static(struct mail_search_arg *arg)
        case SEARCH_FLAGS:
        case SEARCH_KEYWORDS:
        case SEARCH_MODSEQ:
+       case SEARCH_INTHREAD:
                break;
        case SEARCH_ALL:
        case SEARCH_UIDSET:
index 299065cdc863d253fd935d44edf04d869dbd4a98..bf10d44fd45cf4593e6a280533c5e14d512c396f 100644 (file)
@@ -374,6 +374,31 @@ static bool search_arg_build(struct search_build_data *data,
                        return ARG_NEW_HEADER(SEARCH_HEADER, key);
                }
                break;
+       case 'I':
+               if (strcmp(str, "INTHREAD") == 0) {
+                       /* <algorithm> <search key> */
+                       enum mail_thread_type thread_type;
+                       const char *str;
+
+                       if ((*args)->type != IMAP_ARG_ATOM) {
+                               data->error = "Invalid parameter for INTHREAD";
+                               return FALSE;
+                       }
+
+                       str = IMAP_ARG_STR_NONULL(*args);
+                       if (!mail_thread_type_parse(str, &thread_type)) {
+                               data->error = "Unknown thread algorithm";
+                               return FALSE;
+                       }
+                       *args += 1;
+
+                       *next_sarg = search_arg_new(data->pool,
+                                                   SEARCH_INTHREAD);
+                       (*next_sarg)->value.thread_type = thread_type;
+                       subargs = &(*next_sarg)->value.subargs;
+                       return search_arg_build(data, args, subargs);
+               }
+               break;
        case 'K':
                if (strcmp(str, "KEYWORD") == 0) {
                        return ARG_NEW_STR(SEARCH_KEYWORDS);
index 532e2078c1ba568b7de9b7a4ac4741f68a0da6a5..6179bf853c3f03afc612c0a19fded94982c4c05e 100644 (file)
@@ -61,6 +61,7 @@ mail_search_args_init_sub(struct mail_search_args *args,
                          bool change_uidsets,
                          const ARRAY_TYPE(seq_range) *search_saved_uidset)
 {
+       struct mail_search_args *thread_args;
        const char *keywords[2];
 
        for (; arg != NULL; arg = arg->next) {
@@ -85,6 +86,23 @@ mail_search_args_init_sub(struct mail_search_args *args,
                                                              keywords);
                        break;
 
+               case SEARCH_INTHREAD:
+                       thread_args = arg->value.search_args;
+                       if (thread_args == NULL) {
+                               arg->value.search_args = thread_args =
+                                       p_new(args->pool,
+                                             struct mail_search_args, 1);
+                               thread_args->pool = args->pool;
+                               thread_args->args = arg->value.subargs;
+                               thread_args->charset = args->charset;
+                               thread_args->simplified = TRUE;
+                               /* simplification should have unnested all
+                                  inthreads, so we'll assume that
+                                  have_inthreads=FALSE */
+                       }
+                       thread_args->refcount++;
+                       thread_args->box = args->box;
+                       /* fall through */
                case SEARCH_SUB:
                case SEARCH_OR:
                        mail_search_args_init_sub(args, arg->value.subargs,
@@ -124,6 +142,16 @@ static void mail_search_args_deinit_sub(struct mail_search_args *args,
                                break;
                        mailbox_keywords_free(args->box, &arg->value.keywords);
                        break;
+               case SEARCH_INTHREAD:
+                       i_assert(arg->value.search_args->refcount > 0);
+                       arg->value.search_args->refcount--;
+                       arg->value.search_args->box = NULL;
+                       if (args->refcount == 0 &&
+                           arg->value.search_result != NULL) {
+                               mailbox_search_result_free(
+                                       &arg->value.search_result);
+                       }
+                       /* fall through */
                case SEARCH_SUB:
                case SEARCH_OR:
                        mail_search_args_deinit_sub(args, arg->value.subargs);
@@ -164,6 +192,7 @@ static void mail_search_args_seq2uid_sub(struct mail_search_args *args,
                        break;
                case SEARCH_SUB:
                case SEARCH_OR:
+               case SEARCH_INTHREAD:
                        mail_search_args_seq2uid_sub(args, arg->value.subargs,
                                                     uids);
                        break;
@@ -445,9 +474,11 @@ mail_search_args_simplify_sub(struct mail_search_arg *args, bool parent_and)
                        continue;
                }
 
-               if (args->type == SEARCH_SUB || args->type == SEARCH_OR) {
+               if (args->type == SEARCH_SUB ||
+                   args->type == SEARCH_OR ||
+                   args->type == SEARCH_INTHREAD) {
                        mail_search_args_simplify_sub(args->value.subargs,
-                                                     args->type == SEARCH_SUB);
+                                                     args->type != SEARCH_OR);
                }
 
                /* merge all flags arguments */
@@ -507,8 +538,80 @@ mail_search_args_simplify_sub(struct mail_search_arg *args, bool parent_and)
        }
 }
 
+static bool
+mail_search_args_unnest_inthreads(struct mail_search_args *args,
+                                 struct mail_search_arg **argp,
+                                 bool parent_inthreads, bool parent_and)
+{
+       struct mail_search_arg *arg, *thread_arg, *or_arg;
+       bool child_inthreads = FALSE, non_inthreads = FALSE;
+
+       for (arg = *argp; arg != NULL; arg = arg->next) {
+               switch (arg->type) {
+               case SEARCH_SUB:
+               case SEARCH_OR:
+                       if (!mail_search_args_unnest_inthreads(args,
+                                       &arg->value.subargs, parent_inthreads,
+                                       arg->type != SEARCH_OR)) {
+                               arg->result = 1;
+                               child_inthreads = TRUE;
+                       } else {
+                               arg->result = 0;
+                               non_inthreads = TRUE;
+                       }
+                       break;
+               case SEARCH_INTHREAD:
+                       if (mail_search_args_unnest_inthreads(args,
+                                       &arg->value.subargs, TRUE, TRUE)) {
+                               /* children converted to SEARCH_INTHREADs */
+                               arg->type = SEARCH_SUB;
+                       }
+                       args->have_inthreads = TRUE;
+                       arg->result = 1;
+                       child_inthreads = TRUE;
+                       break;
+               default:
+                       arg->result = 0;
+                       non_inthreads = TRUE;
+                       break;
+               }
+       }
+
+       if (!parent_inthreads || !child_inthreads || !non_inthreads)
+               return FALSE;
+
+       /* put all non-INTHREADs under a single INTHREAD */
+       thread_arg = p_new(args->pool, struct mail_search_arg, 1);
+       thread_arg->type = SEARCH_INTHREAD;
+
+       while (*argp != NULL) {
+               arg = *argp;
+               argp = &(*argp)->next;
+
+               if (arg->result == 0) {
+                       /* not an INTHREAD or a SUB/OR with only INTHREADs */
+                       arg->next = thread_arg->value.subargs;
+                       thread_arg->value.subargs = arg;
+               }
+       }
+       if (!parent_and) {
+               /* We want to OR the args */
+               or_arg = p_new(args->pool, struct mail_search_arg, 1);
+               or_arg->type = SEARCH_OR;
+               or_arg->value.subargs = thread_arg->value.subargs;
+               thread_arg->value.subargs = or_arg;
+       }
+       return TRUE;
+}
+
 void mail_search_args_simplify(struct mail_search_args *args)
 {
        args->simplified = TRUE;
+
        mail_search_args_simplify_sub(args->args, TRUE);
+       if (mail_search_args_unnest_inthreads(args, &args->args,
+                                             FALSE, TRUE)) {
+               /* we may have added some extra SUBs that could be dropped */
+               mail_search_args_simplify_sub(args->args, TRUE);
+       }
 }
index 8cccb03308fc8a2f127f244423361add9185e91a..6cc1c1a171f12d1563832e56cd9b572297902f4f 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "seq-range-array.h"
 #include "mail-types.h"
+#include "mail-thread.h"
 
 enum mail_search_arg_type {
        SEARCH_OR,
@@ -41,7 +42,8 @@ enum mail_search_arg_type {
        SEARCH_TEXT_FAST,
 
        /* extensions */
-       SEARCH_MODSEQ
+       SEARCH_MODSEQ,
+       SEARCH_INTHREAD
 };
 
 enum mail_search_arg_flag {
@@ -72,9 +74,12 @@ struct mail_search_arg {
                time_t time;
                uoff_t size;
                enum mail_flags flags;
+               enum mail_search_arg_flag search_flags;
+               enum mail_thread_type thread_type;
                struct mail_keywords *keywords;
                struct mail_search_modseq *modseq;
-               enum mail_search_arg_flag search_flags;
+               struct mail_search_args *search_args;
+               struct mail_search_result *search_result;
        } value;
 
         void *context;