summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/index/index-search-result.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-storage/index/index-search-result.c')
-rw-r--r--src/lib-storage/index/index-search-result.c194
1 files changed, 194 insertions, 0 deletions
diff --git a/src/lib-storage/index/index-search-result.c b/src/lib-storage/index/index-search-result.c
new file mode 100644
index 0000000..6bce84f
--- /dev/null
+++ b/src/lib-storage/index/index-search-result.c
@@ -0,0 +1,194 @@
+/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "seq-range-array.h"
+#include "mail-search.h"
+#include "mailbox-search-result-private.h"
+#include "index-storage.h"
+#include "index-search-result.h"
+
+static void
+search_result_range_remove(struct mail_search_result *result,
+ const ARRAY_TYPE(seq_range) *changed_uids_arr,
+ unsigned int *idx,
+ uint32_t *next_uid, uint32_t last_uid)
+{
+ const struct seq_range *uids;
+ unsigned int i, count;
+ uint32_t uid;
+
+ /* remove full seq_ranges */
+ uid = *next_uid;
+ uids = array_get(changed_uids_arr, &count);
+ for (i = *idx; uids[i].seq2 < last_uid;) {
+ i_assert(uids[i].seq1 <= uid);
+ for (; uid <= uids[i].seq2; uid++)
+ mailbox_search_result_remove(result, uid);
+ i++;
+ i_assert(i < count);
+ uid = uids[i].seq1;
+ }
+
+ /* remove the last seq_range */
+ i_assert(uids[i].seq1 <= uid && uids[i].seq2 >= last_uid);
+ for (; uid <= last_uid; uid++)
+ mailbox_search_result_remove(result, uid);
+
+ if (uid > uids[i].seq2) {
+ /* finished this range */
+ if (++i < count)
+ uid = uids[i].seq1;
+ else {
+ /* this was the last searched message */
+ uid = 0;
+ }
+ }
+
+ *next_uid = uid;
+ *idx = i;
+}
+
+static int
+search_result_update_search(struct mail_search_result *result,
+ const ARRAY_TYPE(seq_range) *changed_uids_arr)
+{
+ struct mailbox_transaction_context *t;
+ struct mail_search_context *search_ctx;
+ struct mail *mail;
+ const struct seq_range *changed_uids;
+ unsigned int changed_count, changed_idx;
+ uint32_t next_uid;
+ int ret;
+
+ changed_uids = array_get(changed_uids_arr, &changed_count);
+ i_assert(changed_count > 0);
+ next_uid = changed_uids[0].seq1;
+ changed_idx = 0;
+
+ mail_search_args_init(result->search_args, result->box, FALSE, NULL);
+
+ t = mailbox_transaction_begin(result->box, 0, __func__);
+ search_ctx = mailbox_search_init(t, result->search_args, NULL, 0, NULL);
+ /* tell search that we're updating an existing search result,
+ so it can do some optimizations based on it */
+ search_ctx->update_result = result;
+
+ while (mailbox_search_next(search_ctx, &mail)) {
+ i_assert(next_uid != 0);
+
+ if (next_uid != mail->uid) {
+ /* some messages in changed_uids didn't match.
+ make sure they don't exist in the search result. */
+ search_result_range_remove(result, changed_uids_arr,
+ &changed_idx, &next_uid,
+ mail->uid-1);
+ i_assert(next_uid == mail->uid);
+ }
+ if (changed_uids[changed_idx].seq2 > next_uid) {
+ next_uid++;
+ } else if (++changed_idx < changed_count) {
+ next_uid = changed_uids[changed_idx].seq1;
+ } else {
+ /* this was the last searched message */
+ next_uid = 0;
+ }
+ /* match - make sure it exists in search result */
+ mailbox_search_result_add(result, mail->uid);
+ }
+ mail_search_args_deinit(result->search_args);
+ ret = mailbox_search_deinit(&search_ctx);
+
+ if (next_uid != 0 && ret == 0) {
+ /* last message(s) didn't match. make sure they don't exist
+ in the search result. */
+ search_result_range_remove(result, changed_uids_arr,
+ &changed_idx, &next_uid,
+ changed_uids[changed_count-1].seq2);
+ }
+
+ if (mailbox_transaction_commit(&t) < 0)
+ ret = -1;
+ return ret;
+}
+
+int index_search_result_update_flags(struct mail_search_result *result,
+ const ARRAY_TYPE(seq_range) *uids)
+{
+ struct mail_search_arg search_arg;
+ int ret;
+
+ if (array_count(uids) == 0)
+ return 0;
+
+ /* add a temporary search parameter to limit the search only to
+ the changed messages */
+ i_zero(&search_arg);
+ search_arg.type = SEARCH_UIDSET;
+ search_arg.value.seqset = *uids;
+ search_arg.next = result->search_args->args;
+ result->search_args->args = &search_arg;
+ ret = search_result_update_search(result, uids);
+ i_assert(result->search_args->args == &search_arg);
+ result->search_args->args = search_arg.next;
+ return ret;
+}
+
+int index_search_result_update_appends(struct mail_search_result *result,
+ unsigned int old_messages_count)
+{
+ struct mailbox_transaction_context *t;
+ struct mail_search_context *search_ctx;
+ struct mail *mail;
+ struct mail_search_arg search_arg;
+ uint32_t message_count;
+ int ret;
+
+ message_count = mail_index_view_get_messages_count(result->box->view);
+ if (old_messages_count == message_count) {
+ /* no new messages */
+ return 0;
+ }
+
+ /* add a temporary search parameter to limit the search only to
+ the new messages */
+ i_zero(&search_arg);
+ search_arg.type = SEARCH_SEQSET;
+ t_array_init(&search_arg.value.seqset, 1);
+ seq_range_array_add_range(&search_arg.value.seqset,
+ old_messages_count + 1, message_count);
+ search_arg.next = result->search_args->args;
+ result->search_args->args = &search_arg;
+
+ /* add all messages matching the search to search result */
+ t = mailbox_transaction_begin(result->box, 0, __func__);
+ search_ctx = mailbox_search_init(t, result->search_args, NULL, 0, NULL);
+
+ while (mailbox_search_next(search_ctx, &mail))
+ mailbox_search_result_add(result, mail->uid);
+
+ ret = mailbox_search_deinit(&search_ctx);
+ if (mailbox_transaction_commit(&t) < 0)
+ ret = -1;
+
+ i_assert(result->search_args->args == &search_arg);
+ result->search_args->args = search_arg.next;
+ return ret;
+}
+
+void index_search_results_update_expunges(struct mailbox *box,
+ const ARRAY_TYPE(seq_range) *expunges)
+{
+ const struct seq_range *seqs;
+ uint32_t seq, uid;
+
+ if (array_count(&box->search_results) == 0)
+ return;
+
+ array_foreach(expunges, seqs) {
+ for (seq = seqs->seq1; seq <= seqs->seq2; seq++) {
+ mail_index_lookup_uid(box->view, seq, &uid);
+ mailbox_search_results_remove(box, uid);
+ }
+ }
+}