diff src/lib-storage/index/index-search.c @ 1915:79790750c349 HEAD

importing new index code. mbox still broken.
author Timo Sirainen <tss@iki.fi>
date Tue, 27 Apr 2004 23:25:52 +0300
parents bd8b6ed35327
children 68938dccbc45
line wrap: on
line diff
--- a/src/lib-storage/index/index-search.c	Tue Apr 27 23:14:15 2004 +0300
+++ b/src/lib-storage/index/index-search.c	Tue Apr 27 23:25:52 2004 +0300
@@ -7,12 +7,10 @@
 #include "message-date.h"
 #include "message-body-search.h"
 #include "message-header-search.h"
+#include "message-parser.h"
 #include "imap-date.h"
 #include "index-storage.h"
-#include "index-messageset.h"
 #include "index-mail.h"
-#include "mail-custom-flags.h"
-#include "mail-modifylog.h"
 #include "mail-search.h"
 
 #include <stdlib.h>
@@ -21,12 +19,14 @@
 #define TXT_UNKNOWN_CHARSET "[BADCHARSET] Unknown charset"
 #define TXT_INVALID_SEARCH_KEY "Invalid search key"
 
-struct mail_search_context {
+struct index_search_context {
+        struct mail_search_context mail_ctx;
+	struct index_transaction_context *trans;
 	struct index_mailbox *ibox;
 	char *charset;
 	struct mail_search_arg *args;
 
-	struct messageset_context *msgset_ctx;
+	uint32_t seq1, seq2;
 	struct index_mail imail;
 	struct mail *mail;
 
@@ -37,7 +37,7 @@
 };
 
 struct search_header_context {
-        struct mail_search_context *index_context;
+        struct index_search_context *index_context;
 	struct mail_search_arg *args;
 
         struct message_header_line *hdr;
@@ -47,70 +47,17 @@
 };
 
 struct search_body_context {
-        struct mail_search_context *index_ctx;
+        struct index_search_context *index_ctx;
 	struct istream *input;
 	const struct message_part *part;
 };
 
-static int msgset_contains(const char *set, unsigned int match_num,
-			   unsigned int max_num)
+static int seqset_contains(struct mail_search_seqset *set, uint32_t seq)
 {
-	unsigned int num, num2;
-
-	while (*set != '\0') {
-		if (*set == '*') {
-			set++;
-			num = max_num;
-		} else {
-			num = 0;
-			while (*set >= '0' && *set <= '9') {
-				num = num*10 + (*set-'0');
-				set++;
-			}
-
-			if (num == 0)
-				return FALSE;
-		}
-
-		if (*set == ',' || *set == '\0') {
-			if (num == match_num)
-				return TRUE;
-			if (*set == '\0')
-				return FALSE;
-		} else if (*set == ':') {
-			set++;
-
-			if (*set == '*') {
-				set++;
-
-				if (match_num >= num && num <= max_num)
-					return TRUE;
-			} else {
-				num2 = 0;
-				while (*set >= '0' && *set <= '9') {
-					num2 = num2*10 + (*set-'0');
-					set++;
-				}
-
-				if (num2 == 0)
-					return FALSE;
-
-				if (num > num2) {
-					/* swap, as specified by RFC-3501 */
-					unsigned int temp = num;
-					num = num2;
-					num2 = temp;
-				}
-
-				if (match_num >= num && match_num <= num2)
-					return TRUE;
-			}
-
-			if (*set != ',')
-				return FALSE;
-		}
-
-		set++;
+	while (set != NULL) {
+		if (seq >= set->seq1 && seq <= set->seq2)
+			return TRUE;
+		set = set->next;
 	}
 
 	return FALSE;
@@ -133,56 +80,56 @@
 }
 
 static int search_keyword(struct mail_index *index,
-			  struct mail_index_record *rec, const char *value)
+			  const struct mail_index_record *rec,
+			  const char *value)
 {
 	const char **custom_flags;
 	int i;
 
-	if ((rec->msg_flags & MAIL_CUSTOM_FLAGS_MASK) == 0)
-		return FALSE;
+	for (i = 0; i < INDEX_CUSTOM_FLAGS_BYTE_COUNT; i++) {
+		if (rec->custom_flags[i] != 0)
+			break;
+	}
 
-	custom_flags = mail_custom_flags_list_get(index->custom_flags);
+	if (i == INDEX_CUSTOM_FLAGS_BYTE_COUNT)
+		return FALSE; /* no custom flags set */
+
+	/*FIXME:custom_flags = mail_custom_flags_list_get(index->custom_flags);
 	for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++) {
 		if (custom_flags[i] != NULL &&
 		    strcasecmp(custom_flags[i], value) == 0) {
 			return rec->msg_flags &
 				(1 << (MAIL_CUSTOM_FLAG_1_BIT+i));
 		}
-	}
+	}*/
 
 	return FALSE;
 }
 
 /* Returns >0 = matched, 0 = not matched, -1 = unknown */
 static int search_arg_match_index(struct index_mailbox *ibox,
-				  struct mail_index_record *rec,
-				  unsigned int client_seq,
+				  const struct mail_index_record *rec,
 				  enum mail_search_arg_type type,
 				  const char *value)
 {
 	switch (type) {
 	case SEARCH_ALL:
 		return 1;
-	case SEARCH_SET:
-		return msgset_contains(value, client_seq,
-				       ibox->synced_messages_count);
-	case SEARCH_UID:
-		return msgset_contains(value, rec->uid,
-				       ibox->index->header->next_uid-1);
 
 	/* flags */
 	case SEARCH_ANSWERED:
-		return rec->msg_flags & MAIL_ANSWERED;
+		return rec->flags & MAIL_ANSWERED;
 	case SEARCH_DELETED:
-		return rec->msg_flags & MAIL_DELETED;
+		return rec->flags & MAIL_DELETED;
 	case SEARCH_DRAFT:
-		return rec->msg_flags & MAIL_DRAFT;
+		return rec->flags & MAIL_DRAFT;
 	case SEARCH_FLAGGED:
-		return rec->msg_flags & MAIL_FLAGGED;
+		return rec->flags & MAIL_FLAGGED;
 	case SEARCH_SEEN:
-		return rec->msg_flags & MAIL_SEEN;
+		return rec->flags & MAIL_SEEN;
 	case SEARCH_RECENT:
-		return rec->uid >= ibox->index->first_recent_uid;
+		//FIXME:return rec->uid >= ibox->index->first_recent_uid;
+		return FALSE;
 	case SEARCH_KEYWORD:
 		return search_keyword(ibox->index, rec, value);
 
@@ -193,10 +140,22 @@
 
 static void search_index_arg(struct mail_search_arg *arg, void *context)
 {
-	struct mail_search_context *ctx = context;
+	struct index_search_context *ctx = context;
+	int found;
+
+	if (arg->type == SEARCH_SEQSET) {
+		found = seqset_contains(arg->value.seqset, ctx->mail->seq);
+		ARG_SET_RESULT(arg, found);
+		return;
+	}
+
+	if (ctx->imail.data.rec == NULL) {
+		/* expunged message */
+		ARG_SET_RESULT(arg, 0);
+		return;
+	}
 
 	switch (search_arg_match_index(ctx->ibox, ctx->imail.data.rec,
-				       ctx->mail->seq,
 				       arg->type, arg->value.str)) {
 	case -1:
 		/* unknown */
@@ -211,7 +170,7 @@
 }
 
 /* Returns >0 = matched, 0 = not matched, -1 = unknown */
-static int search_arg_match_cached(struct mail_search_context *ctx,
+static int search_arg_match_cached(struct index_search_context *ctx,
 				   enum mail_search_arg_type type,
 				   const char *value)
 {
@@ -291,7 +250,7 @@
 
 static void search_cached_arg(struct mail_search_arg *arg, void *context)
 {
-	struct mail_search_context *ctx = context;
+	struct index_search_context *ctx = context;
 
 	switch (search_arg_match_cached(ctx, arg->type,
 					arg->value.str)) {
@@ -340,7 +299,7 @@
 }
 
 static struct header_search_context *
-search_header_context(struct mail_search_context *ctx,
+search_header_context(struct index_search_context *ctx,
 		      struct mail_search_arg *arg)
 {
 	int unknown_charset;
@@ -517,7 +476,7 @@
 }
 
 static int search_arg_match_text(struct mail_search_arg *args,
-				 struct mail_search_context *ctx)
+				 struct index_search_context *ctx)
 {
 	struct istream *input;
 	const char *const *headers;
@@ -571,249 +530,180 @@
 	return TRUE;
 }
 
-static int seq_update(const char *set, unsigned int *first_seq,
-		      unsigned int *last_seq, unsigned int max_value)
+static int search_msgset_fix(struct index_mailbox *ibox,
+                             const struct mail_index_header *hdr,
+			     struct mail_search_seqset *set,
+			     uint32_t *seq1_r, uint32_t *seq2_r)
 {
-	unsigned int seq;
-	int first = TRUE;
+	for (; set != NULL; set = set->next) {
+		if (set->seq1 == (uint32_t)-1)
+			set->seq1 = hdr->messages_count;
+		if (set->seq2 == (uint32_t)-1)
+			set->seq2 = hdr->messages_count;
 
-	while (*set != '\0') {
-		if (*set == '*') {
-			seq = max_value;
-			set++;
-		} else {
-			seq = 0;
-			while (*set >= '0' && *set <= '9') {
-				seq = seq*10 + (*set-'0');
-				set++;
-			}
+		if (set->seq1 == 0 || set->seq2 == 0 ||
+		    set->seq1 > hdr->messages_count ||
+		    set->seq2 > hdr->messages_count) {
+			mail_storage_set_syntax_error(ibox->box.storage,
+						      "Invalid messageset");
+			return -1;
 		}
 
-		if (seq == 0)
-			return FALSE;
-
-		if (*first_seq == 0 || seq < *first_seq)
-			*first_seq = seq;
-		if (*last_seq == 0 || seq > *last_seq)
-			*last_seq = seq;
-
-		if (*set != '\0') {
-			if (*set == ',')
-				first = TRUE;
-			else if (*set == ':' && first)
-				first = FALSE;
-			else
-				return FALSE;
-			set++;
-		}
+		if (*seq1_r > set->seq1 || *seq1_r == 0)
+			*seq1_r = set->seq1;
+		if (*seq2_r < set->seq2)
+			*seq2_r = set->seq2;
 	}
-
-	return TRUE;
+	return 0;
 }
 
-struct search_msgset_context {
-	struct index_mailbox *ibox;
-
-	unsigned int first_seq, last_seq;
-	unsigned int first_uid, last_uid;
+static int search_parse_msgset_args(struct index_mailbox *ibox,
+				    struct mail_search_arg *args,
+				    uint32_t *seq1_r, uint32_t *seq2_r)
+{
+	const struct mail_index_header *hdr;
 
-	struct mail_search_arg *msgset_arg;
-	unsigned int msgset_arg_count;
-};
+	*seq1_r = *seq2_r = 0;
 
-static int search_parse_msgset_args(struct search_msgset_context *ctx,
-				    struct mail_search_arg *args)
-{
-	struct index_mailbox *ibox = ctx->ibox;
-
+	hdr = mail_index_get_header(ibox->view);
 	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_parse_msgset_args(ctx, args->value.subargs))
-				return FALSE;
-		} else if (args->type == SEARCH_SET) {
-			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",
-					args->value.str);
-				return FALSE;
-			}
-		} else if (args->type == SEARCH_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",
-					args->value.str);
-				return FALSE;
-			}
+			if (search_parse_msgset_args(ibox, args->value.subargs,
+						     seq1_r, seq2_r) < 0)
+				return -1;
+		} else if (args->type == SEARCH_OR) {
+			/* FIXME: in cases like "SEEN OR 5 7" we shouldn't
+			   limit the range, but in cases like "1 OR 5 7" we
+			   should expand the range. A bit tricky, we'll
+			   just go through everything now to make it work
+			   right. */
+			*seq1_r = 1;
+			*seq2_r = hdr->messages_count;
+
+                        /* We still have to fix potential seqsets though */
+			if (search_parse_msgset_args(ibox, args->value.subargs,
+						     seq1_r, seq2_r) < 0)
+				return -1;
+		} else if (args->type == SEARCH_SEQSET) {
+			if (search_msgset_fix(ibox, hdr, args->value.seqset,
+					      seq1_r, seq2_r) < 0)
+				return -1;
 		} else if (args->type == SEARCH_ALL) {
-			/* go through everything */
-			ctx->first_seq = 1;
-			ctx->last_seq = ibox->synced_messages_count;
-			ctx->msgset_arg_count++;
-			return TRUE;
+			/* go through everything. don't stop, have to fix
+			   seqsets. */
+			*seq1_r = 1;
+			*seq2_r = hdr->messages_count;
 		}
 	}
+	return 0;
+}
 
-	return TRUE;
+static int search_limit_lowwater(struct index_mailbox *ibox,
+				 uint32_t uid_lowwater, uint32_t *first_seq)
+{
+        const struct mail_index_header *hdr;
+	uint32_t seq1, seq2;
+
+	if (uid_lowwater == 0)
+		return 0;
+
+	hdr = mail_index_get_header(ibox->view);
+	if (mail_index_lookup_uid_range(ibox->view, uid_lowwater,
+					hdr->next_uid-1,
+					&seq1, &seq2) < 0) {
+		mail_storage_set_index_error(ibox);
+		return -1;
+	}
+
+	if (*first_seq < seq1)
+		*first_seq = seq1;
+	return 0;
 }
 
 static int search_limit_by_flags(struct index_mailbox *ibox,
 				 struct mail_search_arg *args,
-				 unsigned int *first_uid,
-				 unsigned int *last_uid)
+				 uint32_t *seq1, uint32_t *seq2)
 {
-	struct mail_index_header *hdr;
-	unsigned int uid;
+	const struct mail_index_header *hdr;
 
-	hdr = ibox->index->header;
+	hdr = mail_index_get_header(ibox->view);
 	for (; args != NULL; args = args->next) {
 		if (args->type == SEARCH_SEEN) {
 			/* SEEN with 0 seen? */
 			if (!args->not && hdr->seen_messages_count == 0)
-				return FALSE;
+				return 0;
 
 			if (hdr->seen_messages_count == hdr->messages_count) {
 				/* UNSEEN with all seen? */
 				if (args->not)
-					return FALSE;
+					return 0;
 
 				/* SEEN with all seen */
 				args->match_always = TRUE;
-			} else {
+			} else if (args->not) {
 				/* UNSEEN with lowwater limiting */
-				uid = hdr->first_unseen_uid_lowwater;
-				if (args->not && *first_uid < uid)
-					*first_uid = uid;
+				if (search_limit_lowwater(ibox,
+                                		hdr->first_unseen_uid_lowwater,
+						seq1) < 0)
+					return -1;
 			}
 		}
 
 		if (args->type == SEARCH_DELETED) {
 			/* DELETED with 0 deleted? */
 			if (!args->not && hdr->deleted_messages_count == 0)
-				return FALSE;
+				return 0;
 
 			if (hdr->deleted_messages_count ==
 			    hdr->messages_count) {
 				/* UNDELETED with all deleted? */
 				if (args->not)
-					return FALSE;
+					return 0;
 
 				/* DELETED with all deleted */
 				args->match_always = TRUE;
-			} else {
+			} else if (!args->not) {
 				/* DELETED with lowwater limiting */
-				uid = hdr->first_deleted_uid_lowwater;
-				if (!args->not && *first_uid < uid)
-					*first_uid = uid;
+				if (search_limit_lowwater(ibox,
+                                		hdr->first_deleted_uid_lowwater,
+						seq1) < 0)
+					return -1;
 			}
 		}
 
-		if (args->type == SEARCH_RECENT) {
+		/*FIXME:if (args->type == SEARCH_RECENT) {
 			uid = ibox->index->first_recent_uid;
 			if (!args->not && *first_uid < uid)
 				*first_uid = ibox->index->first_recent_uid;
 			else if (args->not && *last_uid >= uid)
 				*last_uid = uid-1;
-		}
-	}
-
-	return *first_uid <= *last_uid;
-}
-
-static int client_seq_to_uid(struct index_mailbox *ibox,
-			     unsigned int seq, unsigned int *uid)
-{
-	struct mail_index_record *rec;
-	unsigned int expunges_before;
-
-	if (seq > ibox->synced_messages_count) {
-		mail_storage_set_syntax_error(ibox->box.storage,
-					      "Sequence out of range: %u", seq);
-		return FALSE;
+		}*/
 	}
 
-	if (mail_modifylog_seq_get_expunges(ibox->index->modifylog, seq, seq,
-					    &expunges_before) == NULL)
-		return FALSE;
-
-	seq -= expunges_before;
-
-	rec = ibox->index->lookup(ibox->index, seq);
-	*uid = rec == NULL ? 0 : rec->uid;
-	return TRUE;
+	return *seq1 <= *seq2;
 }
 
-static int search_get_msgset(struct index_mailbox *ibox,
-			     struct mail_search_arg *args,
-			     struct messageset_context **msgset_r)
+static int search_get_seqset(struct index_search_context *ctx,
+			     struct mail_search_arg *args)
 {
-        struct search_msgset_context ctx;
-	unsigned int uid;
+        const struct mail_index_header *hdr;
 
-	memset(&ctx, 0, sizeof(ctx));
-	ctx.ibox = ibox;
-
-	if (!search_parse_msgset_args(&ctx, args))
+	if (search_parse_msgset_args(ctx->ibox, args,
+				     &ctx->seq1, &ctx->seq2) < 0)
 		return -1;
 
-	/* seq_update() should make sure that these can't happen */
-	i_assert(ctx.first_seq <= ctx.last_seq);
-	i_assert(ctx.first_uid <= ctx.last_uid);
-
-	if (ctx.first_seq > 1) {
-		if (!client_seq_to_uid(ibox, ctx.first_seq, &uid))
-			return -1;
-		if (uid == 0)
-			return 0;
-
-		if (ctx.first_uid == 0 || uid < ctx.first_uid)
-			ctx.first_uid = 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 (ctx.last_uid == 0 || uid > ctx.last_uid)
-			ctx.last_uid = uid;
+	if (ctx->seq1 == 0) {
+		hdr = mail_index_get_header(ctx->ibox->view);
+		ctx->seq1 = 1;
+		ctx->seq2 = hdr->messages_count;
 	}
 
-	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;
+	i_assert(ctx->seq1 <= ctx->seq2);
 
 	/* UNSEEN and DELETED in root search level may limit the range */
-	if (!search_limit_by_flags(ibox, args, &ctx.first_uid, &ctx.last_uid))
-		return 0;
-
-	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;
+	if (search_limit_by_flags(ctx->ibox, args, &ctx->seq1, &ctx->seq2) < 0)
+		return -1;
+	return 0;
 }
 
 int index_storage_search_get_sorting(struct mailbox *box __attr_unused__,
@@ -821,80 +711,58 @@
 {
 	/* currently we don't support sorting */
 	*sort_program = MAIL_SORT_END;
-	return TRUE;
+	return 0;
 }
 
 struct mail_search_context *
-index_storage_search_init(struct mailbox *box, const char *charset,
-			  struct mail_search_arg *args,
+index_storage_search_init(struct mailbox_transaction_context *_t,
+			  const char *charset, struct mail_search_arg *args,
 			  const enum mail_sort_type *sort_program,
 			  enum mail_fetch_field wanted_fields,
 			  const char *const wanted_headers[])
 {
-	struct index_mailbox *ibox = (struct index_mailbox *) box;
-	struct mail_search_context *ctx;
+	struct index_transaction_context *t =
+		(struct index_transaction_context *)_t;
+	struct index_search_context *ctx;
 
 	if (sort_program != NULL && *sort_program != MAIL_SORT_END) {
-		i_error("BUG: index_storage_search_init(): "
-			"invalid sort_program");
-		return NULL;
+		i_fatal("BUG: index_storage_search_init(): "
+			 "invalid sort_program");
 	}
 
-	if (!index_storage_sync_and_lock(ibox, TRUE, TRUE, MAIL_LOCK_SHARED))
-		return NULL;
+	/*FIXME:if (!index_storage_sync_and_lock(ibox, TRUE, TRUE, MAIL_LOCK_SHARED))
+		return NULL;*/
 
-	ctx = i_new(struct mail_search_context, 1);
-	ctx->ibox = ibox;
+	ctx = i_new(struct index_search_context, 1);
+	ctx->mail_ctx.box = &t->ibox->box;
+	ctx->trans = t;
+	ctx->ibox = t->ibox;
 	ctx->charset = i_strdup(charset);
 	ctx->args = args;
 
-	ctx->mail = (struct mail *) &ctx->imail;
-	index_mail_init(ibox, &ctx->imail, wanted_fields, wanted_headers);
-
-	if (ibox->synced_messages_count == 0)
-		return ctx;
+	ctx->mail = &ctx->imail.mail;
+	index_mail_init(t, &ctx->imail, wanted_fields, wanted_headers);
 
 	mail_search_args_reset(ctx->args, TRUE);
 
-	/* see if we can limit the records we look at */
-	switch (search_get_msgset(ibox, args, &ctx->msgset_ctx)) {
-	case -1:
-		/* error */
+	if (search_get_seqset(ctx, args) < 0) {
 		ctx->failed = TRUE;
-		return ctx;
-	case 0:
-		/* nothing found */
-		return ctx;
+		ctx->seq1 = 1;
+		ctx->seq2 = 0;
 	}
-
-	return ctx;
+	return &ctx->mail_ctx;
 }
 
-int index_storage_search_deinit(struct mail_search_context *ctx, int *all_found)
+int index_storage_search_deinit(struct mail_search_context *_ctx)
 {
-	int ret, msgset_ret;
-
-	ret = !ctx->failed && ctx->error == NULL;
+        struct index_search_context *ctx = (struct index_search_context *)_ctx;
+	int ret;
 
-	if (ctx->msgset_ctx != NULL) {
-		msgset_ret = index_messageset_deinit(ctx->msgset_ctx);
-		if (msgset_ret < 0)
-			ret = FALSE;
-		if (all_found != NULL)
-			*all_found = msgset_ret > 0;
-	} else {
-		if (all_found != NULL)
-			*all_found = !ctx->failed;
-	}
+	ret = ctx->failed || ctx->error != NULL ? -1 : 0;
 
-	if (ctx->ibox->fetch_mail.pool != NULL)
-		index_mail_deinit(&ctx->ibox->fetch_mail);
 	if (ctx->imail.pool != NULL)
 		index_mail_deinit(&ctx->imail);
 
-	if (!index_storage_lock(ctx->ibox, MAIL_LOCK_UNLOCK))
-		ret = FALSE;
-
 	if (ctx->error != NULL) {
 		mail_storage_set_error(ctx->ibox->box.storage,
 				       "%s", ctx->error);
@@ -907,7 +775,7 @@
 	return ret;
 }
 
-static int search_match_next(struct mail_search_context *ctx)
+static int search_match_next(struct index_search_context *ctx)
 {
         struct mail_search_arg *arg;
 	int ret;
@@ -918,6 +786,12 @@
 	if (ret >= 0)
 		return ret > 0;
 
+	if (ctx->imail.data.rec == NULL) {
+		/* expunged message, no way to check if the rest would have
+		   matched */
+		return FALSE;
+	}
+
 	/* next search only from cached arguments */
 	ret = mail_search_args_foreach(ctx->args, search_cached_arg, ctx);
 	if (ret >= 0)
@@ -935,33 +809,27 @@
 	return TRUE;
 }
 
-struct mail *index_storage_search_next(struct mail_search_context *ctx)
+struct mail *index_storage_search_next(struct mail_search_context *_ctx)
 {
-	const struct messageset_mail *msgset_mail;
+        struct index_search_context *ctx = (struct index_search_context *)_ctx;
+	const struct mail_index_record *rec;
 	int ret;
 
-	if (ctx->msgset_ctx == NULL) {
-		/* initialization failed or didn't found any messages */
-		return NULL;
-	}
-
-	do {
-		msgset_mail = index_messageset_next(ctx->msgset_ctx);
-		if (msgset_mail == NULL) {
-			ret = -1;
-			break;
+	ret = 0;
+	while (ctx->seq1 <= ctx->seq2) {
+		if (mail_index_lookup(ctx->ibox->view, ctx->seq1, &rec) < 0) {
+			ctx->failed = TRUE;
+			mail_storage_set_index_error(ctx->ibox);
+			return NULL;
 		}
 
-		ctx->mail->seq = msgset_mail->client_seq;
-		ctx->mail->uid = msgset_mail->rec->uid;
+		ctx->imail.data.rec = rec;
+		ctx->mail->seq = ctx->seq1++;
+		ctx->mail->uid = rec == NULL ? 0 : rec->uid;
 
-		ret = index_mail_next(&ctx->imail, msgset_mail->rec,
-				      msgset_mail->idx_seq, TRUE);
-		if (ret <= 0) {
-			if (ret < 0)
-				break;
-			continue;
-		}
+		ret = index_mail_next(&ctx->imail, rec, ctx->mail->seq, TRUE);
+		if (ret < 0)
+			break;
 
 		t_push();
 		ret = search_match_next(ctx);
@@ -969,11 +837,12 @@
 
 		if (ctx->error != NULL)
 			ret = -1;
-	} while (ret == 0);
+		if (ret != 0)
+			break;
+	}
 
-	if (ret < 0) {
+	if (ret <= 0) {
 		/* error or last record */
-		index_mail_deinit(&ctx->imail);
 		return NULL;
 	}