From: Timo Sirainen Date: Fri, 20 Jun 2008 02:37:30 +0000 (+0300) Subject: Added non-optimized support for SEARCH INTHREAD. X-Git-Tag: 1.2.alpha1~252 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a9aa2f70dd3f16ca139546a12f04089af937052c;p=thirdparty%2Fdovecot%2Fcore.git Added non-optimized support for SEARCH INTHREAD. --HG-- branch : HEAD --- diff --git a/src/lib-storage/index/index-search.c b/src/lib-storage/index/index-search.c index 416807f4fe..03e34f8793 100644 --- a/src/lib-storage/index/index-search.c +++ b/src/lib-storage/index/index-search.c @@ -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: diff --git a/src/lib-storage/mail-search-build.c b/src/lib-storage/mail-search-build.c index 299065cdc8..bf10d44fd4 100644 --- a/src/lib-storage/mail-search-build.c +++ b/src/lib-storage/mail-search-build.c @@ -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) { + /* */ + 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); diff --git a/src/lib-storage/mail-search.c b/src/lib-storage/mail-search.c index 532e2078c1..6179bf853c 100644 --- a/src/lib-storage/mail-search.c +++ b/src/lib-storage/mail-search.c @@ -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); + } } diff --git a/src/lib-storage/mail-search.h b/src/lib-storage/mail-search.h index 8cccb03308..6cc1c1a171 100644 --- a/src/lib-storage/mail-search.h +++ b/src/lib-storage/mail-search.h @@ -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;