6903
|
1 /* Copyright (c) 2006-2007 Dovecot authors, see the included COPYING file */
|
|
2
|
|
3 #include "lib.h"
|
|
4 #include "array.h"
|
6911
|
5 #include "str.h"
|
6903
|
6 #include "seq-range-array.h"
|
6911
|
7 #include "charset-utf8.h"
|
6903
|
8 #include "mail-search.h"
|
|
9 #include "mail-storage-private.h"
|
|
10 #include "fts-api-private.h"
|
|
11 #include "fts-storage.h"
|
|
12
|
|
13 static void
|
|
14 uid_range_to_seqs(struct mailbox *box, const ARRAY_TYPE(seq_range) *uid_range,
|
|
15 ARRAY_TYPE(seq_range) *seq_range)
|
|
16 {
|
|
17 const struct seq_range *range;
|
|
18 struct seq_range new_range;
|
|
19 unsigned int i, count;
|
|
20
|
|
21 range = array_get(uid_range, &count);
|
|
22 i_array_init(seq_range, count);
|
|
23 for (i = 0; i < count; i++) {
|
|
24 mailbox_get_uids(box, range[i].seq1, range[i].seq2,
|
|
25 &new_range.seq1, &new_range.seq2);
|
|
26 if (new_range.seq1 != 0)
|
|
27 array_append(seq_range, &new_range, 1);
|
|
28 }
|
|
29 }
|
|
30
|
|
31 static void fts_uid_results_to_seq(struct fts_search_context *fctx)
|
|
32 {
|
|
33 ARRAY_TYPE(seq_range) uid_range;
|
|
34
|
|
35 uid_range = fctx->definite_seqs;
|
|
36 i_array_init(&fctx->definite_seqs, array_count(&uid_range));
|
|
37 uid_range_to_seqs(fctx->t->box, &uid_range, &fctx->definite_seqs);
|
|
38 array_free(&uid_range);
|
|
39
|
|
40 uid_range = fctx->maybe_seqs;
|
|
41 i_array_init(&fctx->maybe_seqs, array_count(&uid_range));
|
|
42 uid_range_to_seqs(fctx->t->box, &uid_range, &fctx->maybe_seqs);
|
|
43 array_free(&uid_range);
|
|
44 }
|
|
45
|
|
46 static int fts_search_lookup_arg(struct fts_search_context *fctx,
|
|
47 struct mail_search_arg *arg, bool filter)
|
|
48 {
|
|
49 struct fts_backend *backend;
|
|
50 enum fts_lookup_flags flags = 0;
|
|
51 const char *key;
|
6911
|
52 string_t *key_utf8;
|
|
53 enum charset_result result;
|
|
54 int ret;
|
6903
|
55
|
|
56 switch (arg->type) {
|
|
57 case SEARCH_HEADER:
|
|
58 /* we can filter out messages that don't have the header,
|
|
59 but we can't trust definite results list. */
|
|
60 flags = FTS_LOOKUP_FLAG_HEADER;
|
|
61 backend = fctx->fbox->backend_substr;
|
|
62 key = arg->value.str;
|
|
63 if (*key == '\0') {
|
|
64 /* we're only checking the existence
|
|
65 of the header. */
|
|
66 key = arg->hdr_field_name;
|
|
67 }
|
|
68 break;
|
|
69 case SEARCH_TEXT:
|
|
70 case SEARCH_TEXT_FAST:
|
|
71 flags = FTS_LOOKUP_FLAG_HEADER;
|
|
72 case SEARCH_BODY:
|
|
73 case SEARCH_BODY_FAST:
|
|
74 flags |= FTS_LOOKUP_FLAG_BODY;
|
|
75 key = arg->value.str;
|
|
76 backend = fctx->fbox->backend_fast != NULL &&
|
|
77 (arg->type == SEARCH_TEXT_FAST ||
|
|
78 arg->type == SEARCH_BODY_FAST) ?
|
|
79 fctx->fbox->backend_fast : fctx->fbox->backend_substr;
|
|
80 break;
|
|
81 default:
|
|
82 /* can't filter this */
|
|
83 i_assert(filter);
|
|
84 return 0;
|
|
85 }
|
|
86 if (arg->not)
|
|
87 flags |= FTS_LOOKUP_FLAG_INVERT;
|
|
88
|
6911
|
89 /* convert key to titlecase */
|
|
90 t_push();
|
|
91 key_utf8 = t_str_new(128);
|
|
92 if (charset_to_utf8_str(fctx->charset, CHARSET_FLAG_DECOMP_TITLECASE,
|
|
93 key, key_utf8, &result) < 0) {
|
|
94 /* unknown charset, can't handle this */
|
|
95 ret = 0;
|
|
96 } else if (result != CHARSET_RET_OK) {
|
|
97 /* let the core code handle this error */
|
|
98 ret = 0;
|
|
99 } else if (!backend->locked && fts_backend_lock(backend) <= 0)
|
|
100 ret = -1;
|
|
101 else if (!filter) {
|
|
102 ret = fts_backend_lookup(backend, str_c(key_utf8), flags,
|
|
103 &fctx->definite_seqs,
|
|
104 &fctx->maybe_seqs);
|
|
105 } else {
|
|
106 ret = fts_backend_filter(backend, str_c(key_utf8), flags,
|
|
107 &fctx->definite_seqs,
|
|
108 &fctx->maybe_seqs);
|
6903
|
109 }
|
6911
|
110 t_pop();
|
|
111 return ret;
|
6903
|
112 }
|
|
113
|
|
114 void fts_search_lookup(struct fts_search_context *fctx)
|
|
115 {
|
|
116 struct mail_search_arg *arg;
|
|
117 int ret;
|
|
118
|
|
119 if (fctx->best_arg == NULL)
|
|
120 return;
|
|
121
|
|
122 i_array_init(&fctx->definite_seqs, 64);
|
|
123 i_array_init(&fctx->maybe_seqs, 64);
|
|
124
|
|
125 /* start filtering with the best arg */
|
|
126 ret = fts_search_lookup_arg(fctx, fctx->best_arg, FALSE);
|
|
127 /* filter the rest */
|
|
128 for (arg = fctx->args; arg != NULL && ret == 0; arg = arg->next) {
|
|
129 if (arg != fctx->best_arg)
|
|
130 ret = fts_search_lookup_arg(fctx, arg, TRUE);
|
|
131 }
|
|
132
|
|
133 if (fctx->fbox->backend_fast != NULL &&
|
|
134 fctx->fbox->backend_fast->locked)
|
|
135 fts_backend_unlock(fctx->fbox->backend_fast);
|
|
136 if (fctx->fbox->backend_substr != NULL &&
|
|
137 fctx->fbox->backend_substr->locked)
|
|
138 fts_backend_unlock(fctx->fbox->backend_substr);
|
|
139
|
|
140 if (ret == 0) {
|
|
141 fctx->seqs_set = TRUE;
|
|
142 fts_uid_results_to_seq(fctx);
|
|
143 }
|
|
144 }
|
|
145
|
|
146 static bool arg_is_better(const struct mail_search_arg *new_arg,
|
|
147 const struct mail_search_arg *old_arg)
|
|
148 {
|
|
149 if (old_arg == NULL)
|
|
150 return TRUE;
|
|
151 if (new_arg == NULL)
|
|
152 return FALSE;
|
|
153
|
|
154 /* avoid NOTs */
|
|
155 if (old_arg->not && !new_arg->not)
|
|
156 return TRUE;
|
|
157 if (!old_arg->not && new_arg->not)
|
|
158 return FALSE;
|
|
159
|
|
160 /* prefer not to use headers. they have a larger possibility of
|
|
161 having lots of identical strings */
|
|
162 if (old_arg->type == SEARCH_HEADER)
|
|
163 return TRUE;
|
|
164 else if (new_arg->type == SEARCH_HEADER)
|
|
165 return FALSE;
|
|
166
|
|
167 return strlen(new_arg->value.str) > strlen(old_arg->value.str);
|
|
168 }
|
|
169
|
|
170 static void
|
|
171 fts_search_args_find_best(struct mail_search_arg *args,
|
|
172 struct mail_search_arg **best_fast_arg,
|
|
173 struct mail_search_arg **best_substr_arg)
|
|
174 {
|
|
175 for (; args != NULL; args = args->next) {
|
|
176 switch (args->type) {
|
|
177 case SEARCH_BODY_FAST:
|
|
178 case SEARCH_TEXT_FAST:
|
|
179 if (arg_is_better(args, *best_fast_arg))
|
|
180 *best_fast_arg = args;
|
|
181 break;
|
|
182 case SEARCH_BODY:
|
|
183 case SEARCH_TEXT:
|
|
184 case SEARCH_HEADER:
|
|
185 if (arg_is_better(args, *best_substr_arg))
|
|
186 *best_substr_arg = args;
|
|
187 break;
|
|
188 default:
|
|
189 break;
|
|
190 }
|
|
191 }
|
|
192 }
|
|
193
|
|
194 void fts_search_analyze(struct fts_search_context *fctx)
|
|
195 {
|
|
196 struct mail_search_arg *best_fast_arg = NULL, *best_substr_arg = NULL;
|
|
197
|
|
198 fts_search_args_find_best(fctx->args, &best_fast_arg, &best_substr_arg);
|
|
199
|
|
200 if (best_fast_arg != NULL && fctx->fbox->backend_fast != NULL) {
|
|
201 /* use fast backend whenever possible */
|
|
202 fctx->best_arg = best_fast_arg;
|
|
203 fctx->build_backend = fctx->fbox->backend_fast;
|
|
204 } else if (best_fast_arg != NULL || best_substr_arg != NULL) {
|
|
205 fctx->build_backend = fctx->fbox->backend_substr;
|
|
206 fctx->best_arg = arg_is_better(best_substr_arg, best_fast_arg) ?
|
|
207 best_substr_arg : best_fast_arg;
|
|
208 }
|
|
209 }
|