mail-index-transaction.c revision 9270ca3c33f27e2d1bbe4171941dab82be7cb0e3
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2003-2008 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen/* Inside transaction we keep messages stored in sequences in uid fields.
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen Before they're written to transaction log the sequences are changed to
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen UIDs. This is because we're able to compress sequence ranges better. */
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen#include "lib.h"
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen#include "ioloop.h"
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen#include "array.h"
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen#include "bsearch-insert-pos.h"
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen#include "seq-range-array.h"
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen#include "mail-index-view-private.h"
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen#include "mail-transaction-log.h"
c977374bd4651cafc1626ebe308aa66dfd8b30e0Timo Sirainen#include "mail-cache-private.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mail-index-transaction-private.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <stddef.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <stdlib.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <time.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid (*hook_mail_index_transaction_created)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (struct mail_index_transaction *t) = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmail_index_transaction_has_ext_changes(struct mail_index_transaction *t);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainenvoid mail_index_transaction_reset(struct mail_index_transaction *t)
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ARRAY_TYPE(seq_array) *recs;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_index_transaction_ext_hdr_update **ext_hdrs;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned i, count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&t->ext_rec_updates)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen recs = array_get_modifiable(&t->ext_rec_updates, &count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&recs[i]))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&recs[i]);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&t->ext_rec_updates);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&t->ext_hdr_updates)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ext_hdrs = array_get_modifiable(&t->ext_hdr_updates, &count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < count; i++)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free(ext_hdrs[i]);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen array_free(&t->ext_hdr_updates);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&t->keyword_updates)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_index_transaction_keyword_update *u;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen u = array_get_modifiable(&t->keyword_updates, &count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&u[i].add_seq))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&u[i].add_seq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&u[i].remove_seq))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&u[i].remove_seq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&t->keyword_updates);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&t->keyword_resets))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&t->keyword_resets);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&t->appends))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&t->appends);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&t->expunges))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&t->expunges);
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen if (array_is_created(&t->updates))
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen array_free(&t->updates);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&t->ext_resizes))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&t->ext_resizes);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&t->ext_resets))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&t->ext_resets);
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen if (array_is_created(&t->ext_reset_ids))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_free(&t->ext_reset_ids);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t->first_new_seq = mail_index_view_get_messages_count(t->view)+1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t->last_new_seq = 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t->last_update_idx = 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memset(t->pre_hdr_mask, 0, sizeof(t->pre_hdr_mask));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memset(t->post_hdr_mask, 0, sizeof(t->post_hdr_mask));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (t->cache_trans_ctx != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_cache_transaction_rollback(&t->cache_trans_ctx);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t->appends_nonsorted = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t->pre_hdr_changed = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t->post_hdr_changed = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t->reset = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t->log_updates = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t->log_ext_updates = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool mail_index_transaction_has_changes(struct mail_index_transaction *t)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* flag updates aren't included in log_updates */
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen return array_is_created(&t->appends) ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_is_created(&t->expunges) ||
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen array_is_created(&t->keyword_resets) ||
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen array_is_created(&t->keyword_updates) ||
d22301419109ed4a38351715e6760011421dadecTimo Sirainen t->pre_hdr_changed || t->post_hdr_changed;
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void mail_index_transaction_free(struct mail_index_transaction *t)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_transaction_reset(t);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen array_free(&t->module_contexts);
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen mail_index_view_transaction_unref(t->view);
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen mail_index_view_close(&t->view);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free(t);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct mail_index_view *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmail_index_transaction_get_view(struct mail_index_transaction *t)
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen{
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen return t->view;
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen}
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenbool mail_index_transaction_is_expunged(struct mail_index_transaction *t,
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen uint32_t seq)
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen{
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen return array_is_created(&t->expunges) &&
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen seq_range_exists(&t->expunges, seq);
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainenvoid mail_index_transaction_ref(struct mail_index_transaction *t)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
637455ebee0453f860c9bce0626c485e35fb83deTimo Sirainen t->refcount++;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid mail_index_transaction_unref(struct mail_index_transaction **_t)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct mail_index_transaction *t = *_t;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *_t = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (--t->refcount == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_transaction_free(t);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int mail_index_seq_record_cmp(const void *key, const void *data)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const uint32_t *seq_p = key;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const uint32_t *data_seq = data;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return *seq_p - *data_seq;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainenbool mail_index_seq_array_lookup(const ARRAY_TYPE(seq_array) *array,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uint32_t seq, unsigned int *idx_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const void *base;
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen unsigned int count;
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen base = array_get(array, &count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return bsearch_insert_pos(&seq, base, count, array->arr.element_size,
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen mail_index_seq_record_cmp, idx_r);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainenstatic bool mail_index_seq_array_add(ARRAY_TYPE(seq_array) *array, uint32_t seq,
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen const void *record, size_t record_size,
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen void *old_record)
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen{
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen void *p;
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen unsigned int idx, aligned_record_size;
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen /* records need to be 32bit aligned */
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen aligned_record_size = (record_size + 3) & ~3;
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen if (!array_is_created(array)) {
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen array_create(array, default_pool,
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen sizeof(seq) + aligned_record_size,
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen 1024 / (sizeof(seq) + aligned_record_size));
73a87c2ff65c6116cde6fb158dfddb8ef7346901Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(array->arr.element_size == sizeof(seq) + aligned_record_size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mail_index_seq_array_lookup(array, seq, &idx)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* already there, update */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen p = array_idx_modifiable(array, idx);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (old_record != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* save the old record before overwriting it */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memcpy(old_record, PTR_OFFSET(p, sizeof(seq)),
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen record_size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memcpy(PTR_OFFSET(p, sizeof(seq)), record, record_size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* insert */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen p = array_insert_space(array, idx);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen memcpy(p, &seq, sizeof(seq));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memcpy(PTR_OFFSET(p, sizeof(seq)), record, record_size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic uint32_t
d22301419109ed4a38351715e6760011421dadecTimo Sirainenmail_index_transaction_get_uid(struct mail_index_transaction *t, uint32_t seq)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct mail_index_record *rec;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(seq > 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (seq >= t->first_new_seq)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen rec = mail_index_transaction_lookup(t, seq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen i_assert(seq <= t->view->map->hdr.messages_count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen rec = MAIL_INDEX_MAP_IDX(t->view->map, seq - 1);
3f91e60401495a4046c73992fabaa5e77200a451Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(rec->uid != 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return rec->uid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmail_index_convert_to_uids(struct mail_index_transaction *t,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ARRAY_TYPE(seq_array) *array)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uint32_t *seq;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen unsigned int i, count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!array_is_created(array))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen count = array_count(array);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen seq = array_idx_modifiable(array, i);
3f91e60401495a4046c73992fabaa5e77200a451Timo Sirainen *seq = mail_index_transaction_get_uid(t, *seq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic uint32_t
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenget_nonexpunged_uid2(struct mail_index_transaction *t,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uint32_t uid1, uint32_t seq1)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen seq1++;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen while (mail_index_transaction_get_uid(t, seq1) == uid1 + 1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen seq1++;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uid1++;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return uid1;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmail_index_convert_to_uid_ranges(struct mail_index_transaction *t,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ARRAY_TYPE(seq_range) *array)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct seq_range *range, *new_range;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int i, count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uint32_t uid1, uid2;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!array_is_created(array))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen count = array_count(array);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen range = array_idx_modifiable(array, i);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uid1 = mail_index_transaction_get_uid(t, range->seq1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uid2 = mail_index_transaction_get_uid(t, range->seq2);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (uid2 - uid1 == range->seq2 - range->seq1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* simple conversion */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen range->seq1 = uid1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen range->seq2 = uid2;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* remove expunged UIDs */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen new_range = array_insert_space(array, i);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen range = array_idx_modifiable(array, i + 1);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen count++;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memcpy(new_range, range, array->arr.element_size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen new_range->seq1 = uid1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen new_range->seq2 = get_nonexpunged_uid2(t, uid1,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen range->seq1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* continue the range without the inserted seqs */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen range->seq1 += new_range->seq2 - new_range->seq1 + 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void keyword_updates_convert_to_uids(struct mail_index_transaction *t)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_index_transaction_keyword_update *updates;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int i, count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!array_is_created(&t->keyword_updates))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen updates = array_get_modifiable(&t->keyword_updates, &count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < count; i++) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen mail_index_convert_to_uid_ranges(t, &updates[i].add_seq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_convert_to_uid_ranges(t, &updates[i].remove_seq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmail_index_transaction_convert_to_uids(struct mail_index_transaction *t)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ARRAY_TYPE(seq_array) *updates;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int i, count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (array_is_created(&t->ext_rec_updates)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen updates = array_get_modifiable(&t->ext_rec_updates, &count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < count; i++)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_convert_to_uids(t, (void *)&updates[i]);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen keyword_updates_convert_to_uids(t);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_convert_to_uid_ranges(t, &t->expunges);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_convert_to_uid_ranges(t, (void *)&t->updates);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_convert_to_uid_ranges(t, &t->keyword_resets);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct uid_map {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uint32_t idx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uint32_t uid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen};
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int uid_map_cmp(const void *p1, const void *p2)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct uid_map *m1 = p1, *m2 = p2;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return m1->uid < m2->uid ? -1 :
d22301419109ed4a38351715e6760011421dadecTimo Sirainen (m1->uid > m2->uid ? 1 : 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmail_index_update_day_headers(struct mail_index_transaction *t)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_index_header hdr;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct mail_index_record *rec;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const int max_days = N_ELEMENTS(hdr.day_first_uid);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct tm tm;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen time_t stamp;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen int i, days;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen hdr = *mail_index_get_header(t->view);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen rec = array_idx(&t->appends, 0);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen /* get beginning of today */
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen tm = *localtime(&ioloop_time);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen tm.tm_hour = 0;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen tm.tm_min = 0;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen tm.tm_sec = 0;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen stamp = mktime(&tm);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen i_assert(stamp != (time_t)-1);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen if ((time_t)hdr.day_stamp >= stamp)
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen return;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen /* get number of days since last message */
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen days = (stamp - hdr.day_stamp) / (3600*24);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen if (days > max_days)
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen days = max_days;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen /* @UNSAFE: move days forward and fill the missing days with old
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen day_first_uid[0]. */
d1c08db8f581444104233f2380b4591eca836f6aTimo Sirainen memmove(hdr.day_first_uid + days, hdr.day_first_uid, max_days - days);
d1c08db8f581444104233f2380b4591eca836f6aTimo Sirainen for (i = 1; i < days; i++)
d1c08db8f581444104233f2380b4591eca836f6aTimo Sirainen hdr.day_first_uid[i] = hdr.day_first_uid[0];
d1c08db8f581444104233f2380b4591eca836f6aTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen hdr.day_stamp = stamp;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen hdr.day_first_uid[0] = rec->uid;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen mail_index_update_header(t,
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen offsetof(struct mail_index_header, day_stamp),
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen &hdr.day_stamp, sizeof(hdr.day_stamp), FALSE);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen mail_index_update_header(t,
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen offsetof(struct mail_index_header, day_first_uid),
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen hdr.day_first_uid, sizeof(hdr.day_first_uid), FALSE);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen}
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainenstatic void
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainenmail_index_transaction_sort_appends_ext(struct mail_index_transaction *t,
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen const uint32_t *old_to_newseq_map)
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen{
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen ARRAY_TYPE(seq_array) *ext_rec_arrays;
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen ARRAY_TYPE(seq_array) *old_array;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen ARRAY_TYPE(seq_array) new_array;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen unsigned int ext_count;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen const uint32_t *ext_rec;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen uint32_t seq;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen unsigned int i, j, count;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen if (!array_is_created(&t->ext_rec_updates))
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen return;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen ext_rec_arrays = array_get_modifiable(&t->ext_rec_updates, &count);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen for (j = 0; j < count; j++) {
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen old_array = &ext_rec_arrays[j];
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen if (!array_is_created(old_array))
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen continue;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen ext_count = array_count(old_array);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen array_create(&new_array, default_pool,
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen old_array->arr.element_size, ext_count);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen for (i = 0; i < ext_count; i++) {
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen ext_rec = array_idx(old_array, i);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen seq = *ext_rec < t->first_new_seq ? *ext_rec :
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen old_to_newseq_map[*ext_rec - t->first_new_seq];
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen mail_index_seq_array_add(&new_array, seq, ext_rec+1,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen old_array->arr.element_size -
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen sizeof(*ext_rec), NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen array_free(old_array);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ext_rec_arrays[j] = new_array;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainensort_appends_seq_range(struct mail_index_transaction *t,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ARRAY_TYPE(seq_range) *array,
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen const uint32_t *old_to_newseq_map)
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen{
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen struct seq_range *range, temp_range;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen ARRAY_TYPE(seq_range) old_seqs;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen uint32_t idx, idx1, idx2;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen unsigned int i, count;
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen range = array_get_modifiable(array, &count);
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen for (i = 0; i < count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (range[i].seq2 >= t->first_new_seq)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (i == count) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* nothing to do */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_array_init(&old_seqs, count - i);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (range[i].seq1 < t->first_new_seq) {
9955f6cba7652469b1d600a3674e8d27dd4e61bdTimo Sirainen temp_range.seq1 = t->first_new_seq;
8bdb2e6bb77bd40c891c39cd7911887bcfda656eTimo Sirainen temp_range.seq2 = range[i].seq2;
8bdb2e6bb77bd40c891c39cd7911887bcfda656eTimo Sirainen array_append(&old_seqs, &temp_range, 1);
8bdb2e6bb77bd40c891c39cd7911887bcfda656eTimo Sirainen range[i].seq2 = t->first_new_seq - 1;
8bdb2e6bb77bd40c891c39cd7911887bcfda656eTimo Sirainen i++;
8bdb2e6bb77bd40c891c39cd7911887bcfda656eTimo Sirainen }
8bdb2e6bb77bd40c891c39cd7911887bcfda656eTimo Sirainen array_append(&old_seqs, &range[i], count - i);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_delete(array, i, count - i);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
range = array_get_modifiable(&old_seqs, &count);
for (i = 0; i < count; i++) {
idx1 = range[i].seq1 - t->first_new_seq;
idx2 = range[i].seq2 - t->first_new_seq;
for (idx = idx1; idx <= idx2; idx++)
seq_range_array_add(array, 0, old_to_newseq_map[idx]);
}
array_free(&old_seqs);
}
static void
mail_index_transaction_sort_appends_keywords(struct mail_index_transaction *t,
const uint32_t *old_to_newseq_map)
{
struct mail_index_transaction_keyword_update *updates;
unsigned int i, count;
if (array_is_created(&t->keyword_updates)) {
updates = array_get_modifiable(&t->keyword_updates, &count);
for (i = 0; i < count; i++) {
if (array_is_created(&updates->add_seq)) {
sort_appends_seq_range(t, &updates[i].add_seq,
old_to_newseq_map);
}
if (array_is_created(&updates->remove_seq)) {
sort_appends_seq_range(t,
&updates[i].remove_seq,
old_to_newseq_map);
}
}
}
if (array_is_created(&t->keyword_resets)) {
sort_appends_seq_range(t, &t->keyword_resets,
old_to_newseq_map);
}
}
void mail_index_transaction_sort_appends(struct mail_index_transaction *t)
{
struct mail_index_record *recs, *sorted_recs;
struct uid_map *new_uid_map;
uint32_t *old_to_newseq_map;
unsigned int i, count;
if (!t->appends_nonsorted)
return;
/* first make a copy of the UIDs and map them to sequences */
recs = array_get_modifiable(&t->appends, &count);
i_assert(count > 0);
new_uid_map = i_new(struct uid_map, count);
for (i = 0; i < count; i++) {
new_uid_map[i].idx = i;
new_uid_map[i].uid = recs[i].uid;
}
/* now sort the UID map */
qsort(new_uid_map, count, sizeof(*new_uid_map), uid_map_cmp);
/* sort mail records */
sorted_recs = i_new(struct mail_index_record, count);
sorted_recs[0] = recs[new_uid_map[0].idx];
for (i = 1; i < count; i++) {
sorted_recs[i] = recs[new_uid_map[i].idx];
if (sorted_recs[i].uid == sorted_recs[i-1].uid)
i_panic("Duplicate UIDs added in transaction");
}
buffer_write(t->appends.arr.buffer, 0, sorted_recs,
sizeof(*sorted_recs) * count);
i_free(sorted_recs);
old_to_newseq_map = i_new(uint32_t, count);
for (i = 0; i < count; i++)
old_to_newseq_map[new_uid_map[i].idx] = i + t->first_new_seq;
i_free(new_uid_map);
mail_index_transaction_sort_appends_ext(t, old_to_newseq_map);
mail_index_transaction_sort_appends_keywords(t, old_to_newseq_map);
i_free(old_to_newseq_map);
t->appends_nonsorted = FALSE;
}
uint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t)
{
const struct mail_index_header *hdr;
const struct mail_index_record *recs;
unsigned int count, offset;
uint32_t next_uid;
next_uid = t->reset ? 1 : t->view->map->hdr.next_uid;
if (array_is_created(&t->appends)) {
/* get next_uid from appends if they have UIDs */
mail_index_transaction_sort_appends(t);
recs = array_get(&t->appends, &count);
if (count > 0 && recs[count-1].uid != 0) {
i_assert(recs[count-1].uid >= next_uid);
next_uid = recs[count-1].uid + 1;
}
}
/* see if it's been updated in pre/post header changes */
offset = offsetof(struct mail_index_header, next_uid);
if (t->post_hdr_mask[offset] != 0) {
hdr = (const void *)t->post_hdr_change;
if (hdr->next_uid > next_uid)
next_uid = hdr->next_uid;
}
if (t->pre_hdr_mask[offset] != 0) {
hdr = (const void *)t->pre_hdr_change;
if (hdr->next_uid > next_uid)
next_uid = hdr->next_uid;
}
return next_uid;
}
static int mail_index_transaction_commit_v(struct mail_index_transaction *t,
uint32_t *log_file_seq_r,
uoff_t *log_file_offset_r)
{
int ret;
i_assert(t->first_new_seq >
mail_index_view_get_messages_count(t->view));
if (t->cache_trans_ctx != NULL)
mail_cache_transaction_commit(&t->cache_trans_ctx);
if (array_is_created(&t->appends)) {
mail_index_transaction_sort_appends(t);
mail_index_update_day_headers(t);
}
if (mail_index_transaction_convert_to_uids(t) < 0)
ret = -1;
else {
ret = mail_transaction_log_append(t, log_file_seq_r,
log_file_offset_r);
}
if (ret == 0 && !t->view->index->syncing) {
/* if we're committing a normal transaction, we want to
have those changes in the index mapping immediately. this
is especially important when committing cache offset
updates.
however if we're syncing the index now, the mapping must
be done later as MAIL_INDEX_SYNC_HANDLER_FILE so that
expunge handlers get run for the newly expunged messages
(and sync handlers that require HANDLER_FILE as well). */
(void)mail_index_refresh(t->view->index);
}
mail_index_transaction_unref(&t);
return ret;
}
static void mail_index_transaction_rollback_v(struct mail_index_transaction *t)
{
if (t->cache_trans_ctx != NULL)
mail_cache_transaction_rollback(&t->cache_trans_ctx);
mail_index_transaction_unref(&t);
}
int mail_index_transaction_commit(struct mail_index_transaction **_t,
uint32_t *log_file_seq_r,
uoff_t *log_file_offset_r)
{
struct mail_index_transaction *t = *_t;
if (mail_index_view_is_inconsistent(t->view)) {
mail_index_transaction_rollback(_t);
return -1;
}
*_t = NULL;
return t->v.commit(t, log_file_seq_r, log_file_offset_r);
}
void mail_index_transaction_rollback(struct mail_index_transaction **_t)
{
struct mail_index_transaction *t = *_t;
*_t = NULL;
t->v.rollback(t);
}
struct mail_index_record *
mail_index_transaction_lookup(struct mail_index_transaction *t, uint32_t seq)
{
i_assert(seq >= t->first_new_seq && seq <= t->last_new_seq);
return array_idx_modifiable(&t->appends, seq - t->first_new_seq);
}
void mail_index_append(struct mail_index_transaction *t, uint32_t uid,
uint32_t *seq_r)
{
struct mail_index_record *rec;
i_assert(!t->no_appends);
t->log_updates = TRUE;
if (!array_is_created(&t->appends))
i_array_init(&t->appends, 32);
/* sequence number is visible only inside given view,
so let it generate it */
if (t->last_new_seq != 0)
*seq_r = ++t->last_new_seq;
else
*seq_r = t->last_new_seq = t->first_new_seq;
rec = array_append_space(&t->appends);
if (uid != 0) {
rec->uid = uid;
if (!t->appends_nonsorted &&
t->last_new_seq != t->first_new_seq) {
/* if previous record's UID is larger than this one,
we'll have to sort the appends later */
rec = mail_index_transaction_lookup(t, *seq_r - 1);
if (rec->uid > uid)
t->appends_nonsorted = TRUE;
else if (rec->uid == uid)
i_panic("Duplicate UIDs added in transaction");
}
}
}
void mail_index_append_assign_uids(struct mail_index_transaction *t,
uint32_t first_uid, uint32_t *next_uid_r)
{
struct mail_index_record *recs;
unsigned int i, count;
i_assert(first_uid != 0);
if (!array_is_created(&t->appends))
return;
recs = array_get_modifiable(&t->appends, &count);
/* find the first mail with uid = 0 */
for (i = 0; i < count; i++) {
if (recs[i].uid == 0)
break;
}
for (; i < count; i++) {
i_assert(recs[i].uid == 0);
recs[i].uid = first_uid++;
}
*next_uid_r = first_uid;
}
static void
mail_index_expunge_last_append(struct mail_index_transaction *t, uint32_t seq)
{
ARRAY_TYPE(seq_array) *seqs;
struct mail_index_transaction_keyword_update *kw_updates;
unsigned int i, idx, count;
i_assert(seq == t->last_new_seq);
/* remove extension updates */
if (array_is_created(&t->ext_rec_updates)) {
seqs = array_get_modifiable(&t->ext_rec_updates, &count);
for (i = 0; i < count; i++) {
if (array_is_created(&seqs[i]) &&
mail_index_seq_array_lookup(&seqs[i], seq, &idx))
array_delete(&seqs[i], idx, 1);
}
t->log_ext_updates = mail_index_transaction_has_ext_changes(t);
}
/* remove keywords */
if (array_is_created(&t->keyword_resets))
seq_range_array_remove(&t->keyword_resets, seq);
if (array_is_created(&t->keyword_updates)) {
kw_updates = array_get_modifiable(&t->keyword_updates, &count);
for (i = 0; i < count; i++) {
if (array_is_created(&kw_updates[i].add_seq)) {
seq_range_array_remove(&kw_updates[i].add_seq,
seq);
}
if (array_is_created(&kw_updates[i].remove_seq)) {
seq_range_array_remove(
&kw_updates[i].remove_seq, seq);
}
}
}
/* and finally remove the append itself */
array_delete(&t->appends, seq - t->first_new_seq, 1);
t->last_new_seq--;
if (t->first_new_seq > t->last_new_seq) {
t->last_new_seq = 0;
t->appends_nonsorted = FALSE;
array_free(&t->appends);
}
t->log_updates = mail_index_transaction_has_changes(t);
}
void mail_index_expunge(struct mail_index_transaction *t, uint32_t seq)
{
i_assert(seq > 0);
if (seq >= t->first_new_seq) {
/* we can handle only the last append. otherwise we'd have to
renumber sequences and that gets tricky. for now this is
enough, since we typically want to expunge all the
appends. */
mail_index_expunge_last_append(t, seq);
} else {
t->log_updates = TRUE;
/* expunges is a sorted array of {seq1, seq2, ..}, .. */
seq_range_array_add(&t->expunges, 128, seq);
}
}
static bool
mail_transaction_update_want_add(struct mail_index_transaction *t,
const struct mail_transaction_flag_update *u)
{
const struct mail_index_record *rec;
uint32_t seq;
if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES) == 0)
return TRUE;
for (seq = u->uid1; seq <= u->uid2; seq++) {
rec = mail_index_lookup(t->view, seq);
if ((rec->flags & u->add_flags) != u->add_flags ||
(rec->flags & u->remove_flags) != 0)
return TRUE;
}
return FALSE;
}
static void
mail_index_insert_flag_update(struct mail_index_transaction *t,
struct mail_transaction_flag_update u,
uint32_t left_idx, uint32_t right_idx)
{
struct mail_transaction_flag_update *updates, tmp_update;
unsigned int count;
uint32_t idx, move;
updates = array_get_modifiable(&t->updates, &count);
i_assert(left_idx <= right_idx && right_idx <= count);
/* find the first update with either overlapping range,
or the update which will come after our insert */
idx = left_idx;
while (left_idx < right_idx) {
idx = (left_idx + right_idx) / 2;
if (updates[idx].uid2 < u.uid1)
left_idx = idx+1;
else if (updates[idx].uid1 > u.uid1)
right_idx = idx;
else
break;
}
if (idx < count && updates[idx].uid2 < u.uid1)
idx++;
/* overlapping ranges, split/merge them */
i_assert(idx == 0 || updates[idx-1].uid2 < u.uid1);
i_assert(idx == count || updates[idx].uid2 >= u.uid1);
for (; idx < count && u.uid2 >= updates[idx].uid1; idx++) {
if (u.uid1 != updates[idx].uid1 &&
(updates[idx].add_flags != u.add_flags ||
updates[idx].remove_flags != u.remove_flags)) {
if (u.uid1 < updates[idx].uid1) {
/* insert new update */
tmp_update = u;
tmp_update.uid2 = updates[idx].uid1 - 1;
move = 0;
} else {
/* split existing update from beginning */
tmp_update = updates[idx];
tmp_update.uid2 = u.uid1 - 1;
updates[idx].uid1 = u.uid1;
move = 1;
}
i_assert(tmp_update.uid1 <= tmp_update.uid2);
i_assert(updates[idx].uid1 <= updates[idx].uid2);
if (mail_transaction_update_want_add(t, &tmp_update)) {
array_insert(&t->updates, idx, &tmp_update, 1);
updates = array_get_modifiable(&t->updates,
&count);
idx += move;
}
} else if (u.uid1 < updates[idx].uid1) {
updates[idx].uid1 = u.uid1;
}
if (u.uid2 < updates[idx].uid2 &&
(updates[idx].add_flags != u.add_flags ||
updates[idx].remove_flags != u.remove_flags)) {
/* split existing update from end */
tmp_update = updates[idx];
tmp_update.uid2 = u.uid2;
updates[idx].uid1 = u.uid2 + 1;
i_assert(tmp_update.uid1 <= tmp_update.uid2);
i_assert(updates[idx].uid1 <= updates[idx].uid2);
if (mail_transaction_update_want_add(t, &tmp_update))
array_insert(&t->updates, idx, &tmp_update, 1);
updates = array_get_modifiable(&t->updates, &count);
}
updates[idx].add_flags =
(updates[idx].add_flags | u.add_flags) &
~u.remove_flags;
updates[idx].remove_flags =
(updates[idx].remove_flags | u.remove_flags) &
~u.add_flags;
u.uid1 = updates[idx].uid2 + 1;
if (updates[idx].add_flags == 0 &&
updates[idx].remove_flags == 0) {
/* we can remove this update completely */
array_delete(&t->updates, idx, 1);
updates = array_get_modifiable(&t->updates, &count);
idx--;
}
if (u.uid1 > u.uid2) {
/* break here before idx++ so last_update_idx is set
correctly */
break;
}
}
i_assert(idx <= count);
if (u.uid1 <= u.uid2) {
i_assert(idx == 0 || updates[idx-1].uid2 < u.uid1);
i_assert(idx == count || updates[idx].uid1 > u.uid2);
if (mail_transaction_update_want_add(t, &u)) {
array_insert(&t->updates, idx, &u, 1);
count++;
}
}
t->last_update_idx = idx == count ? count-1 : idx;
}
static void mail_index_record_modify_flags(struct mail_index_record *rec,
enum modify_type modify_type,
enum mail_flags flags)
{
switch (modify_type) {
case MODIFY_REPLACE:
rec->flags = flags;
break;
case MODIFY_ADD:
rec->flags |= flags;
break;
case MODIFY_REMOVE:
rec->flags &= ~flags;
break;
}
}
void mail_index_update_flags_range(struct mail_index_transaction *t,
uint32_t seq1, uint32_t seq2,
enum modify_type modify_type,
enum mail_flags flags)
{
struct mail_index_record *rec;
struct mail_transaction_flag_update u, *last_update;
unsigned int first_idx, count;
if (seq2 >= t->first_new_seq) {
/* updates for appended messages, modify them directly */
uint32_t seq;
for (seq = I_MAX(t->first_new_seq, seq1); seq <= seq2; seq++) {
rec = mail_index_transaction_lookup(t, seq);
mail_index_record_modify_flags(rec, modify_type, flags);
}
if (seq1 >= t->first_new_seq)
return;
/* range contains also existing messages. update them next. */
seq2 = t->first_new_seq - 1;
}
i_assert(seq1 <= seq2 && seq1 > 0);
i_assert(seq2 <= mail_index_view_get_messages_count(t->view));
memset(&u, 0, sizeof(u));
u.uid1 = seq1;
u.uid2 = seq2;
switch (modify_type) {
case MODIFY_REPLACE:
u.add_flags = flags;
u.remove_flags = ~flags & MAIL_INDEX_FLAGS_MASK;
break;
case MODIFY_ADD:
u.add_flags = flags;
break;
case MODIFY_REMOVE:
u.remove_flags = flags;
break;
}
if (!array_is_created(&t->updates)) {
i_array_init(&t->updates, 256);
if (mail_transaction_update_want_add(t, &u))
array_append(&t->updates, &u, 1);
return;
}
last_update = array_get_modifiable(&t->updates, &count);
if (t->last_update_idx < count) {
/* fast path - hopefully we're updating the next message,
or a message that is to be appended as last update */
last_update += t->last_update_idx;
if (seq1 - 1 == last_update->uid2) {
if (u.add_flags == last_update->add_flags &&
u.remove_flags == last_update->remove_flags &&
(t->last_update_idx + 1 == count ||
last_update[1].uid1 > seq2)) {
/* we can just update the UID range */
if (mail_transaction_update_want_add(t, &u))
last_update->uid2 = seq2;
return;
}
} else if (seq1 > last_update->uid2) {
/* hopefully we can just append it */
t->last_update_idx++;
last_update++;
}
}
if (t->last_update_idx == count) {
if (mail_transaction_update_want_add(t, &u))
array_append(&t->updates, &u, 1);
else if (t->last_update_idx > 0)
t->last_update_idx--;
} else {
i_assert(t->last_update_idx < count);
/* slow path */
if (seq1 > last_update->uid2) {
/* added after this */
first_idx = t->last_update_idx + 1;
} else {
/* added before this or on top of this */
first_idx = 0;
count = t->last_update_idx + 1;
}
mail_index_insert_flag_update(t, u, first_idx, count);
}
}
void mail_index_update_flags(struct mail_index_transaction *t, uint32_t seq,
enum modify_type modify_type,
enum mail_flags flags)
{
mail_index_update_flags_range(t, seq, seq, modify_type, flags);
}
void mail_index_update_header(struct mail_index_transaction *t,
size_t offset, const void *data, size_t size,
bool prepend)
{
i_assert(offset < sizeof(t->pre_hdr_change));
i_assert(size <= sizeof(t->pre_hdr_change) - offset);
t->log_updates = TRUE;
if (prepend) {
t->pre_hdr_changed = TRUE;
memcpy(t->pre_hdr_change + offset, data, size);
for (; size > 0; size--)
t->pre_hdr_mask[offset++] = 1;
} else {
t->post_hdr_changed = TRUE;
memcpy(t->post_hdr_change + offset, data, size);
for (; size > 0; size--)
t->post_hdr_mask[offset++] = 1;
}
}
void mail_index_ext_resize(struct mail_index_transaction *t, uint32_t ext_id,
uint32_t hdr_size, uint16_t record_size,
uint16_t record_align)
{
struct mail_transaction_ext_intro intro;
uint32_t old_record_size, old_record_align;
memset(&intro, 0, sizeof(intro));
/* get ext_id from transaction's map if it's there */
if (!mail_index_map_get_ext_idx(t->view->map, ext_id, &intro.ext_id)) {
/* have to create it */
const struct mail_index_registered_ext *rext;
intro.ext_id = (uint32_t)-1;
rext = array_idx(&t->view->index->extensions, ext_id);
old_record_size = rext->record_size;
old_record_align = rext->record_align;
} else {
const struct mail_index_ext *ext;
ext = array_idx(&t->view->map->extensions, ext_id);
old_record_size = ext->record_size;
old_record_align = ext->record_align;
}
/* allow only header size changes if extension records have already
been changed in transaction */
i_assert(!array_is_created(&t->ext_rec_updates) ||
(old_record_size == record_size &&
old_record_align == record_align));
t->log_ext_updates = TRUE;
if (!array_is_created(&t->ext_resizes))
i_array_init(&t->ext_resizes, ext_id + 2);
intro.hdr_size = hdr_size;
intro.record_size = record_size;
intro.record_align = record_align;
intro.name_size = 1;
array_idx_set(&t->ext_resizes, ext_id, &intro);
}
void mail_index_ext_reset(struct mail_index_transaction *t, uint32_t ext_id,
uint32_t reset_id)
{
i_assert(reset_id != 0);
mail_index_ext_set_reset_id(t, ext_id, reset_id);
if (!array_is_created(&t->ext_resets))
i_array_init(&t->ext_resets, ext_id + 2);
array_idx_set(&t->ext_resets, ext_id, &reset_id);
t->log_ext_updates = TRUE;
}
static bool
mail_index_transaction_has_ext_changes(struct mail_index_transaction *t)
{
unsigned int i, count;
if (array_is_created(&t->ext_rec_updates)) {
const ARRAY_TYPE(seq_array) *array;
array = array_get(&t->ext_rec_updates, &count);
for (i = 0; i < count; i++) {
if (array_is_created(&array[i]))
return TRUE;
}
}
if (array_is_created(&t->ext_hdr_updates)) {
struct mail_index_transaction_ext_hdr_update *const *hdr;
hdr = array_get(&t->ext_hdr_updates, &count);
for (i = 0; i < count; i++) {
if (hdr[i] != NULL)
return TRUE;
}
}
if (array_is_created(&t->ext_resets)) {
const uint32_t *ids;
ids = array_get(&t->ext_resets, &count);
for (i = 0; i < count; i++) {
if (ids[i] != 0)
return TRUE;
}
}
if (array_is_created(&t->ext_resizes)) {
const struct mail_transaction_ext_intro *resizes;
resizes = array_get(&t->ext_resizes, &count);
for (i = 0; i < count; i++) {
if (resizes[i].name_size > 0)
return TRUE;
}
}
return FALSE;
}
void mail_index_ext_set_reset_id(struct mail_index_transaction *t,
uint32_t ext_id, uint32_t reset_id)
{
if (array_is_created(&t->ext_rec_updates) &&
ext_id < array_count(&t->ext_rec_updates)) {
/* if extension records have been updated, clear them */
ARRAY_TYPE(seq_array) *array;
array = array_idx_modifiable(&t->ext_rec_updates, ext_id);
if (array_is_created(array))
array_clear(array);
}
if (array_is_created(&t->ext_hdr_updates) &&
ext_id < array_count(&t->ext_hdr_updates)) {
/* if extension headers have been updated, clear them */
struct mail_index_transaction_ext_hdr_update **hdr;
hdr = array_idx_modifiable(&t->ext_hdr_updates, ext_id);
if (*hdr != NULL)
i_free_and_null(*hdr);
}
if (array_is_created(&t->ext_resets) &&
ext_id < array_count(&t->ext_resets)) {
/* clear resets */
array_idx_clear(&t->ext_resets, ext_id);
}
if (array_is_created(&t->ext_resizes) &&
ext_id < array_count(&t->ext_resizes)) {
/* clear resizes */
array_idx_clear(&t->ext_resizes, ext_id);
}
if (!array_is_created(&t->ext_reset_ids))
i_array_init(&t->ext_reset_ids, ext_id + 2);
array_idx_set(&t->ext_reset_ids, ext_id, &reset_id);
t->log_ext_updates = mail_index_transaction_has_ext_changes(t);
}
void mail_index_update_header_ext(struct mail_index_transaction *t,
uint32_t ext_id, size_t offset,
const void *data, size_t size)
{
struct mail_index_transaction_ext_hdr_update *hdr, **pos;
hdr = i_malloc(sizeof(*hdr) + size);
hdr->ext_id = ext_id;
hdr->offset = offset;
hdr->size = size;
memcpy(hdr + 1, data, size);
if (!array_is_created(&t->ext_hdr_updates))
i_array_init(&t->ext_hdr_updates, ext_id + 2);
pos = array_idx_modifiable(&t->ext_hdr_updates, ext_id);
if (*pos != NULL) {
i_panic("mail_index_update_header_ext() doesn't currently "
"support multiple updates to the same ext header");
}
*pos = hdr;
t->log_ext_updates = TRUE;
}
void mail_index_update_ext(struct mail_index_transaction *t, uint32_t seq,
uint32_t ext_id, const void *data, void *old_data_r)
{
struct mail_index *index = t->view->index;
const struct mail_index_registered_ext *rext;
const struct mail_transaction_ext_intro *intro;
uint16_t record_size;
ARRAY_TYPE(seq_array) *array;
unsigned int count;
i_assert(seq > 0 &&
(seq <= mail_index_view_get_messages_count(t->view) ||
seq <= t->last_new_seq));
i_assert(ext_id < array_count(&index->extensions));
t->log_ext_updates = TRUE;
if (!array_is_created(&t->ext_resizes)) {
intro = NULL;
count = 0;
} else {
intro = array_get(&t->ext_resizes, &count);
}
if (ext_id < count && intro[ext_id].name_size != 0) {
/* resized record */
record_size = intro[ext_id].record_size;
} else {
rext = array_idx(&index->extensions, ext_id);
record_size = rext->record_size;
}
if (!array_is_created(&t->ext_rec_updates))
i_array_init(&t->ext_rec_updates, ext_id + 2);
array = array_idx_modifiable(&t->ext_rec_updates, ext_id);
/* @UNSAFE */
if (!mail_index_seq_array_add(array, seq, data, record_size,
old_data_r)) {
/* not found, clear old_data if it was given */
if (old_data_r != NULL)
memset(old_data_r, 0, record_size);
}
}
struct mail_keywords *
mail_index_keywords_create(struct mail_index *index,
const char *const keywords[])
{
struct mail_keywords *k;
unsigned int src, dest, i, count;
count = str_array_length(keywords);
if (count == 0) {
k = i_new(struct mail_keywords, 1);
k->index = index;
return k;
}
/* @UNSAFE */
k = i_malloc(sizeof(struct mail_keywords) +
(sizeof(k->idx) * (count-1)));
k->index = index;
/* look up the keywords from index. they're never removed from there
so we can permanently store indexes to them. */
for (src = dest = 0; src < count; src++) {
mail_index_keyword_lookup_or_create(index, keywords[src],
&k->idx[dest]);
/* ignore if this is a duplicate */
for (i = 0; i < src; i++) {
if (k->idx[i] == k->idx[dest])
break;
}
if (i == src)
dest++;
}
k->count = dest;
return k;
}
struct mail_keywords *
mail_index_keywords_create_from_indexes(struct mail_index *index,
const ARRAY_TYPE(keyword_indexes)
*keyword_indexes)
{
struct mail_keywords *k;
const unsigned int *indexes;
unsigned int src, dest, i, count;
indexes = array_get(keyword_indexes, &count);
if (count == 0) {
k = i_new(struct mail_keywords, 1);
k->index = index;
return k;
}
/* @UNSAFE */
k = i_malloc(sizeof(struct mail_keywords) +
(sizeof(k->idx) * (count-1)));
k->index = index;
/* copy but skip duplicates */
for (src = dest = 0; src < count; src++) {
for (i = 0; i < src; i++) {
if (k->idx[i] == indexes[src])
break;
}
if (i == src)
k->idx[dest++] = indexes[src];
}
k->count = dest;
return k;
}
void mail_index_keywords_free(struct mail_keywords **keywords)
{
i_free(*keywords);
*keywords = NULL;
}
void mail_index_update_keywords(struct mail_index_transaction *t, uint32_t seq,
enum modify_type modify_type,
struct mail_keywords *keywords)
{
struct mail_index_transaction_keyword_update *u;
unsigned int i, ku_count;
i_assert(seq > 0 &&
(seq <= mail_index_view_get_messages_count(t->view) ||
seq <= t->last_new_seq));
i_assert(keywords->count > 0 || modify_type == MODIFY_REPLACE);
i_assert(keywords->index == t->view->index);
if (!array_is_created(&t->keyword_updates) && keywords->count > 0) {
uint32_t max_idx = keywords->idx[keywords->count-1];
i_array_init(&t->keyword_updates, max_idx + 1);
}
/* Update add_seq and remove_seq arrays which describe the keyword
changes. Don't bother updating remove_seq or keyword resets for
newly added messages since they default to not having any
keywords anyway. */
switch (modify_type) {
case MODIFY_ADD:
for (i = 0; i < keywords->count; i++) {
u = array_idx_modifiable(&t->keyword_updates,
keywords->idx[i]);
seq_range_array_add(&u->add_seq, 16, seq);
if (seq < t->first_new_seq)
seq_range_array_remove(&u->remove_seq, seq);
}
break;
case MODIFY_REMOVE:
for (i = 0; i < keywords->count; i++) {
u = array_idx_modifiable(&t->keyword_updates,
keywords->idx[i]);
seq_range_array_remove(&u->add_seq, seq);
if (seq < t->first_new_seq)
seq_range_array_add(&u->remove_seq, 16, seq);
}
break;
case MODIFY_REPLACE:
/* Remove sequence from all add/remove arrays */
if (array_is_created(&t->keyword_updates)) {
u = array_get_modifiable(&t->keyword_updates,
&ku_count);
for (i = 0; i < ku_count; i++) {
seq_range_array_remove(&u[i].add_seq, seq);
if (seq < t->first_new_seq) {
seq_range_array_remove(
&u[i].remove_seq, seq);
}
}
}
/* Add the wanted keyword back */
for (i = 0; i < keywords->count; i++) {
u = array_idx_modifiable(&t->keyword_updates,
keywords->idx[i]);
seq_range_array_add(&u->add_seq, 16, seq);
}
if (seq < t->first_new_seq)
seq_range_array_add(&t->keyword_resets, 16, seq);
break;
}
t->log_updates = TRUE;
}
void mail_index_reset(struct mail_index_transaction *t)
{
mail_index_transaction_reset(t);
t->reset = TRUE;
}
struct mail_index_transaction_vfuncs trans_vfuncs = {
mail_index_transaction_commit_v,
mail_index_transaction_rollback_v
};
struct mail_index_transaction *
mail_index_transaction_begin(struct mail_index_view *view,
enum mail_index_transaction_flags flags)
{
struct mail_index_transaction *t;
/* don't allow syncing view while there's ongoing transactions */
mail_index_view_transaction_ref(view);
mail_index_view_ref(view);
t = i_new(struct mail_index_transaction, 1);
t->refcount = 1;
t->v = trans_vfuncs;
t->view = view;
t->flags = flags;
t->sync_transaction = view->index_sync_view;
if (view->syncing) {
/* transaction view cannot work if new records are being added
in two places. make sure it doesn't happen. */
t->no_appends = TRUE;
t->first_new_seq = (uint32_t)-1;
} else {
t->first_new_seq =
mail_index_view_get_messages_count(t->view) + 1;
}
i_array_init(&t->module_contexts,
I_MIN(5, mail_index_module_register.id));
if (hook_mail_index_transaction_created != NULL)
hook_mail_index_transaction_created(t);
return t;
}