comparison 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
comparison
equal deleted inserted replaced
8482:1ff129ed866a 8483:b12705704329
1 /* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "mail-search.h"
6 #include "index-storage.h"
7 #include "virtual-storage.h"
8
9 #include <stdlib.h>
10
11 enum virtual_search_state {
12 VIRTUAL_SEARCH_STATE_FAILED = -1,
13 VIRTUAL_SEARCH_STATE_BUILD,
14 VIRTUAL_SEARCH_STATE_RETURN,
15 VIRTUAL_SEARCH_STATE_SORT,
16 VIRTUAL_SEARCH_STATE_SORT_DONE
17 };
18
19 struct virtual_search_record {
20 uint32_t mailbox_id;
21 uint32_t real_uid;
22 uint32_t virtual_seq;
23 };
24
25 struct virtual_search_context {
26 union mail_search_module_context module_ctx;
27
28 ARRAY_TYPE(seq_range) result;
29 struct seq_range_iter result_iter;
30 ARRAY_DEFINE(records, struct virtual_search_record);
31
32 enum virtual_search_state search_state;
33 unsigned int next_result_n;
34 unsigned int next_record_idx;
35 };
36
37 static int virtual_search_record_cmp(const void *p1, const void *p2)
38 {
39 const struct virtual_search_record *r1 = p1, *r2 = p2;
40
41 if (r1->mailbox_id < r2->mailbox_id)
42 return -1;
43 if (r1->mailbox_id > r2->mailbox_id)
44 return 1;
45
46 if (r1->real_uid < r2->real_uid)
47 return -1;
48 if (r1->real_uid > r2->real_uid)
49 return 1;
50 return 0;
51 }
52
53 static int mail_search_get_result(struct mail_search_context *ctx)
54 {
55 const struct mail_search_arg *arg;
56 int ret = 1;
57
58 for (arg = ctx->args->args; arg != NULL; arg = arg->next) {
59 if (arg->result < 0)
60 return -1;
61 if (arg->result == 0)
62 ret = 0;
63 }
64 return ret;
65 }
66
67 static int virtual_search_get_records(struct mail_search_context *ctx,
68 struct virtual_search_context *vctx)
69 {
70 struct virtual_mailbox *mbox =
71 (struct virtual_mailbox *)ctx->transaction->box;
72 const struct virtual_mail_index_record *vrec;
73 struct virtual_search_record srec, *srecs;
74 unsigned int count;
75 const void *data;
76 bool expunged;
77 int ret, result;
78
79 memset(&srec, 0, sizeof(srec));
80 while ((ret = index_storage_search_next_update_seq(ctx)) > 0) {
81 result = mail_search_get_result(ctx);
82 i_assert(result != 0);
83 if (result > 0) {
84 /* full match, no need to check this any further */
85 seq_range_array_add(&vctx->result, 0, ctx->seq);
86 } else {
87 /* possible match, save and check later */
88 mail_index_lookup_ext(mbox->ibox.view, ctx->seq,
89 mbox->virtual_ext_id,
90 &data, &expunged);
91 vrec = data;
92
93 srec.mailbox_id = vrec->mailbox_id;
94 srec.real_uid = vrec->real_uid;
95 srec.virtual_seq = ctx->seq;
96 array_append(&vctx->records, &srec, 1);
97 }
98 mail_search_args_reset(ctx->args->args, FALSE);
99 }
100 srecs = array_get_modifiable(&vctx->records, &count);
101 qsort(srecs, count, sizeof(*srecs), virtual_search_record_cmp);
102 return ret;
103 }
104
105 struct mail_search_context *
106 virtual_search_init(struct mailbox_transaction_context *t,
107 struct mail_search_args *args,
108 const enum mail_sort_type *sort_program)
109 {
110 struct mail_search_context *ctx;
111 struct virtual_search_context *vctx;
112
113 ctx = index_storage_search_init(t, args, sort_program);
114
115 vctx = i_new(struct virtual_search_context, 1);
116 vctx->search_state = VIRTUAL_SEARCH_STATE_BUILD;
117 i_array_init(&vctx->result, 64);
118 i_array_init(&vctx->records, 64);
119 MODULE_CONTEXT_SET(ctx, virtual_storage_module, vctx);
120
121 if (virtual_search_get_records(ctx, vctx) < 0)
122 vctx->search_state = VIRTUAL_SEARCH_STATE_FAILED;
123
124 seq_range_array_iter_init(&vctx->result_iter, &vctx->result);
125 return ctx;
126 }
127
128 int virtual_search_deinit(struct mail_search_context *ctx)
129 {
130 struct virtual_search_context *vctx = VIRTUAL_CONTEXT(ctx);
131
132 array_free(&vctx->result);
133 array_free(&vctx->records);
134 i_free(vctx);
135 return index_storage_search_deinit(ctx);
136 }
137
138 int virtual_search_next_nonblock(struct mail_search_context *ctx,
139 struct mail *mail, bool *tryagain_r)
140 {
141 struct virtual_search_context *vctx = VIRTUAL_CONTEXT(ctx);
142 uint32_t seq;
143 int ret;
144
145 switch (vctx->search_state) {
146 case VIRTUAL_SEARCH_STATE_FAILED:
147 return -1;
148 case VIRTUAL_SEARCH_STATE_BUILD:
149 if (ctx->sort_program == NULL)
150 vctx->search_state = VIRTUAL_SEARCH_STATE_SORT;
151 else
152 vctx->search_state = VIRTUAL_SEARCH_STATE_RETURN;
153 return virtual_search_next_nonblock(ctx, mail, tryagain_r);
154 case VIRTUAL_SEARCH_STATE_RETURN:
155 return index_storage_search_next_nonblock(ctx, mail, tryagain_r);
156 case VIRTUAL_SEARCH_STATE_SORT:
157 /* the messages won't be returned sorted, so we'll have to
158 do it ourself */
159 while ((ret = index_storage_search_next_nonblock(ctx, mail,
160 tryagain_r)) > 0)
161 seq_range_array_add(&vctx->result, 0, mail->seq);
162 if (ret < 0 || *tryagain_r)
163 return ret;
164
165 vctx->next_result_n = 0;
166 vctx->search_state = VIRTUAL_SEARCH_STATE_SORT_DONE;
167 /* fall through */
168 case VIRTUAL_SEARCH_STATE_SORT_DONE:
169 *tryagain_r = FALSE;
170 if (!seq_range_array_iter_nth(&vctx->result_iter,
171 vctx->next_result_n, &seq))
172 return 0;
173 vctx->next_result_n++;
174 mail_set_seq(mail, seq);
175 return 1;
176 }
177 i_unreached();
178 }
179
180 static void search_args_set_full_match(struct mail_search_arg *args)
181 {
182 for (; args != NULL; args = args->next)
183 args->result = 1;
184 }
185
186 bool virtual_search_next_update_seq(struct mail_search_context *ctx)
187 {
188 struct virtual_search_context *vctx = VIRTUAL_CONTEXT(ctx);
189 const struct virtual_search_record *recs;
190 unsigned int count;
191
192 recs = array_get(&vctx->records, &count);
193 if (vctx->next_record_idx < count) {
194 ctx->seq = recs[vctx->next_record_idx++].virtual_seq - 1;
195 if (!index_storage_search_next_update_seq(ctx))
196 i_unreached();
197 return TRUE;
198 }
199
200 if (ctx->sort_program != NULL &&
201 seq_range_array_iter_nth(&vctx->result_iter,
202 vctx->next_result_n, &ctx->seq)) {
203 search_args_set_full_match(ctx->args->args);
204 vctx->next_result_n++;
205 return TRUE;
206 }
207 return FALSE;
208 }