dovecot-1.2: Added non-optimized support for SEARCH INTHREAD.

dovecot at dovecot.org dovecot at dovecot.org
Fri Jun 20 05:37:41 EEST 2008


details:   http://hg.dovecot.org/dovecot-1.2/rev/bf9c51edbc66
changeset: 7909:bf9c51edbc66
user:      Timo Sirainen <tss at iki.fi>
date:      Fri Jun 20 05:37:30 2008 +0300
description:
Added non-optimized support for SEARCH INTHREAD.

diffstat:

4 files changed, 241 insertions(+), 5 deletions(-)
src/lib-storage/index/index-search.c |  103 ++++++++++++++++++++++++++++++++
src/lib-storage/mail-search-build.c  |   25 +++++++
src/lib-storage/mail-search.c        |  109 +++++++++++++++++++++++++++++++++-
src/lib-storage/mail-search.h        |    9 ++

diffs (truncated from 381 to 300 lines):

diff -r 1e69c84a1e5a -r bf9c51edbc66 src/lib-storage/index/index-search.c
--- a/src/lib-storage/index/index-search.c	Fri Jun 20 05:35:05 2008 +0300
+++ b/src/lib-storage/index/index-search.c	Fri Jun 20 05:37:30 2008 +0300
@@ -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_
 		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
 
 	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 ind
 	}
 }
 
+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
 		     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 m
 
 	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 
 	case SEARCH_FLAGS:
 	case SEARCH_KEYWORDS:
 	case SEARCH_MODSEQ:
+	case SEARCH_INTHREAD:
 		break;
 	case SEARCH_ALL:
 	case SEARCH_UIDSET:
diff -r 1e69c84a1e5a -r bf9c51edbc66 src/lib-storage/mail-search-build.c
--- a/src/lib-storage/mail-search-build.c	Fri Jun 20 05:35:05 2008 +0300
+++ b/src/lib-storage/mail-search-build.c	Fri Jun 20 05:37:30 2008 +0300
@@ -374,6 +374,31 @@ static bool search_arg_build(struct sear
 			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);
diff -r 1e69c84a1e5a -r bf9c51edbc66 src/lib-storage/mail-search.c
--- a/src/lib-storage/mail-search.c	Fri Jun 20 05:35:05 2008 +0300
+++ b/src/lib-storage/mail-search.c	Fri Jun 20 05:37:30 2008 +0300
@@ -61,6 +61,7 @@ mail_search_args_init_sub(struct mail_se
 			  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_se
 							      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(
 				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
 			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 mai
 			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 mai
 	}
 }
 
+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;


More information about the dovecot-cvs mailing list