view src/plugins/virtual/virtual-search.c @ 8483:b12705704329 HEAD

Optimized searching with virtual mailboxes. Instead of going through the messages in the virtual mailbox order, go them through one mailbox at a time and in ascending message order within that mailbox.
author Timo Sirainen <tss@iki.fi>
date Sun, 23 Nov 2008 02:40:09 +0200
parents
children f323bf2465bd
line wrap: on
line source

/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "mail-search.h"
#include "index-storage.h"
#include "virtual-storage.h"

#include <stdlib.h>

enum virtual_search_state {
	VIRTUAL_SEARCH_STATE_FAILED = -1,
	VIRTUAL_SEARCH_STATE_BUILD,
	VIRTUAL_SEARCH_STATE_RETURN,
	VIRTUAL_SEARCH_STATE_SORT,
	VIRTUAL_SEARCH_STATE_SORT_DONE
};

struct virtual_search_record {
	uint32_t mailbox_id;
	uint32_t real_uid;
	uint32_t virtual_seq;
};

struct virtual_search_context {
	union mail_search_module_context module_ctx;

	ARRAY_TYPE(seq_range) result;
	struct seq_range_iter result_iter;
	ARRAY_DEFINE(records, struct virtual_search_record);

	enum virtual_search_state search_state;
	unsigned int next_result_n;
	unsigned int next_record_idx;
};

static int virtual_search_record_cmp(const void *p1, const void *p2)
{
	const struct virtual_search_record *r1 = p1, *r2 = p2;

	if (r1->mailbox_id < r2->mailbox_id)
		return -1;
	if (r1->mailbox_id > r2->mailbox_id)
		return 1;

	if (r1->real_uid < r2->real_uid)
		return -1;
	if (r1->real_uid > r2->real_uid)
		return 1;
	return 0;
}

static int mail_search_get_result(struct mail_search_context *ctx)
{
	const struct mail_search_arg *arg;
	int ret = 1;

	for (arg = ctx->args->args; arg != NULL; arg = arg->next) {
		if (arg->result < 0)
			return -1;
		if (arg->result == 0)
			ret = 0;
	}
	return ret;
}

static int virtual_search_get_records(struct mail_search_context *ctx,
				      struct virtual_search_context *vctx)
{
	struct virtual_mailbox *mbox =
		(struct virtual_mailbox *)ctx->transaction->box;
	const struct virtual_mail_index_record *vrec;
	struct virtual_search_record srec, *srecs;
	unsigned int count;
	const void *data;
	bool expunged;
	int ret, result;

	memset(&srec, 0, sizeof(srec));
	while ((ret = index_storage_search_next_update_seq(ctx)) > 0) {
		result = mail_search_get_result(ctx);
		i_assert(result != 0);
		if (result > 0) {
			/* full match, no need to check this any further */
			seq_range_array_add(&vctx->result, 0, ctx->seq);
		} else {
			/* possible match, save and check later */
			mail_index_lookup_ext(mbox->ibox.view, ctx->seq,
					      mbox->virtual_ext_id,
					      &data, &expunged);
			vrec = data;

			srec.mailbox_id = vrec->mailbox_id;
			srec.real_uid = vrec->real_uid;
			srec.virtual_seq = ctx->seq;
			array_append(&vctx->records, &srec, 1);
		}
		mail_search_args_reset(ctx->args->args, FALSE);
	}
	srecs = array_get_modifiable(&vctx->records, &count);
	qsort(srecs, count, sizeof(*srecs), virtual_search_record_cmp);
	return ret;
}

struct mail_search_context *
virtual_search_init(struct mailbox_transaction_context *t,
		    struct mail_search_args *args,
		    const enum mail_sort_type *sort_program)
{
	struct mail_search_context *ctx;
	struct virtual_search_context *vctx;

	ctx = index_storage_search_init(t, args, sort_program);

	vctx = i_new(struct virtual_search_context, 1);
	vctx->search_state = VIRTUAL_SEARCH_STATE_BUILD;
	i_array_init(&vctx->result, 64);
	i_array_init(&vctx->records, 64);
	MODULE_CONTEXT_SET(ctx, virtual_storage_module, vctx);

	if (virtual_search_get_records(ctx, vctx) < 0)
		vctx->search_state = VIRTUAL_SEARCH_STATE_FAILED;

	seq_range_array_iter_init(&vctx->result_iter, &vctx->result);
	return ctx;
}

int virtual_search_deinit(struct mail_search_context *ctx)
{
	struct virtual_search_context *vctx = VIRTUAL_CONTEXT(ctx);

	array_free(&vctx->result);
	array_free(&vctx->records);
	i_free(vctx);
	return index_storage_search_deinit(ctx);
}

int virtual_search_next_nonblock(struct mail_search_context *ctx,
				 struct mail *mail, bool *tryagain_r)
{
	struct virtual_search_context *vctx = VIRTUAL_CONTEXT(ctx);
	uint32_t seq;
	int ret;

	switch (vctx->search_state) {
	case VIRTUAL_SEARCH_STATE_FAILED:
		return -1;
	case VIRTUAL_SEARCH_STATE_BUILD:
		if (ctx->sort_program == NULL)
			vctx->search_state = VIRTUAL_SEARCH_STATE_SORT;
		else
			vctx->search_state = VIRTUAL_SEARCH_STATE_RETURN;
		return virtual_search_next_nonblock(ctx, mail, tryagain_r);
	case VIRTUAL_SEARCH_STATE_RETURN:
		return index_storage_search_next_nonblock(ctx, mail, tryagain_r);
	case VIRTUAL_SEARCH_STATE_SORT:
		/* the messages won't be returned sorted, so we'll have to
		   do it ourself */
		while ((ret = index_storage_search_next_nonblock(ctx, mail,
							tryagain_r)) > 0)
			seq_range_array_add(&vctx->result, 0, mail->seq);
		if (ret < 0 || *tryagain_r)
			return ret;

		vctx->next_result_n = 0;
		vctx->search_state = VIRTUAL_SEARCH_STATE_SORT_DONE;
		/* fall through */
	case VIRTUAL_SEARCH_STATE_SORT_DONE:
		*tryagain_r = FALSE;
		if (!seq_range_array_iter_nth(&vctx->result_iter,
					      vctx->next_result_n, &seq))
			return 0;
		vctx->next_result_n++;
		mail_set_seq(mail, seq);
		return 1;
	}
	i_unreached();
}

static void search_args_set_full_match(struct mail_search_arg *args)
{
	for (; args != NULL; args = args->next)
		args->result = 1;
}

bool virtual_search_next_update_seq(struct mail_search_context *ctx)
{
	struct virtual_search_context *vctx = VIRTUAL_CONTEXT(ctx);
	const struct virtual_search_record *recs;
	unsigned int count;

	recs = array_get(&vctx->records, &count);
	if (vctx->next_record_idx < count) {
		ctx->seq = recs[vctx->next_record_idx++].virtual_seq - 1;
		if (!index_storage_search_next_update_seq(ctx))
			i_unreached();
		return TRUE;
	}

	if (ctx->sort_program != NULL &&
	    seq_range_array_iter_nth(&vctx->result_iter,
				     vctx->next_result_n, &ctx->seq)) {
		search_args_set_full_match(ctx->args->args);
		vctx->next_result_n++;
		return TRUE;
	}
	return FALSE;
}