mail-index-transaction-update.c revision 1731683fb0c6afceb20903b1c0a45912d00ed5da
/* Copyright (c) 2003-2012 Dovecot authors, see the included COPYING file */
/* Inside transaction we keep messages stored in sequences in uid fields.
Before they're written to transaction log the sequences are changed to
UIDs. */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "mail-index-private.h"
#include "mail-index-transaction-private.h"
static bool
struct mail_index_record *
{
}
void mail_index_transaction_reset_v(struct mail_index_transaction *t)
{
if (array_is_created(&t->ext_rec_updates)) {
if (array_is_created(rec))
}
array_free(&t->ext_rec_updates);
}
if (array_is_created(&t->ext_rec_atomics)) {
if (array_is_created(rec))
}
array_free(&t->ext_rec_atomics);
}
if (array_is_created(&t->ext_hdr_updates)) {
}
array_free(&t->ext_hdr_updates);
}
if (array_is_created(&t->keyword_updates)) {
struct mail_index_transaction_keyword_update *u;
array_foreach_modifiable(&t->keyword_updates, u) {
if (array_is_created(&u->add_seq))
array_free(&u->add_seq);
if (array_is_created(&u->remove_seq))
array_free(&u->remove_seq);
}
array_free(&t->keyword_updates);
}
if (array_is_created(&t->appends))
array_free(&t->appends);
if (array_is_created(&t->modseq_updates))
array_free(&t->modseq_updates);
if (array_is_created(&t->expunges))
array_free(&t->expunges);
if (array_is_created(&t->updates))
array_free(&t->updates);
if (array_is_created(&t->ext_resizes))
array_free(&t->ext_resizes);
if (array_is_created(&t->ext_resets))
array_free(&t->ext_resets);
if (array_is_created(&t->ext_reset_ids))
array_free(&t->ext_reset_ids);
if (array_is_created(&t->ext_reset_atomic))
array_free(&t->ext_reset_atomic);
t->last_new_seq = 0;
t->last_update_idx = 0;
t->min_flagupdate_seq = 0;
t->max_flagupdate_seq = 0;
t->min_highest_modseq = 0;
t->appends_nonsorted = FALSE;
t->expunges_nonsorted = FALSE;
t->pre_hdr_changed = FALSE;
t->post_hdr_changed = FALSE;
t->index_deleted = FALSE;
t->index_undeleted = FALSE;
t->log_updates = FALSE;
t->log_ext_updates = FALSE;
}
void mail_index_transaction_set_log_updates(struct mail_index_transaction *t)
{
/* flag updates aren't included in log_updates */
array_is_created(&t->modseq_updates) ||
array_is_created(&t->expunges) ||
array_is_created(&t->keyword_updates) ||
t->pre_hdr_changed || t->post_hdr_changed ||
t->min_highest_modseq != 0;
}
void mail_index_update_day_headers(struct mail_index_transaction *t)
{
struct mail_index_header hdr;
const struct mail_index_record *rec;
int i, days;
/* get beginning of today */
return;
/* get number of days since last message */
/* @UNSAFE: move days forward and fill the missing days with old
day_first_uid[0]. */
for (i = 1; i < days; i++)
}
{
struct mail_index_record *rec;
i_assert(!t->no_appends);
t->log_updates = TRUE;
if (!array_is_created(&t->appends))
/* 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
if (uid != 0) {
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 */
t->appends_nonsorted = TRUE;
i_panic("Duplicate UIDs added in transaction");
}
if (t->highest_append_uid < uid)
t->highest_append_uid = uid;
}
}
void mail_index_append_finish_uids(struct mail_index_transaction *t,
{
struct mail_index_record *recs;
unsigned int i, count;
if (!array_is_created(&t->appends))
return;
/* first find the highest assigned uid */
for (i = 0; i < count; i++) {
}
/* assign missing uids */
for (i = 0; i < count; i++) {
else {
t->appends_nonsorted = TRUE;
}
}
/* write the saved uids range */
for (i = 1; i < count; i++) {
else {
}
}
}
{
struct mail_transaction_modseq_update *u;
/* modseq=1 is the minimum always and it's only for mails that were
if (min_modseq <= 1)
return;
if (!array_is_created(&t->modseq_updates))
u = array_append_space(&t->modseq_updates);
t->log_updates = TRUE;
}
void mail_index_update_highest_modseq(struct mail_index_transaction *t,
{
/* modseq=1 is the minimum always and it's only for mails that were
if (min_modseq <= 1)
return;
if (t->min_highest_modseq < min_modseq)
t->min_highest_modseq = min_modseq;
t->log_updates = TRUE;
}
static void
{
unsigned int idx;
if (!array_is_created(ext_updates))
return;
if (array_is_created(seqs) &&
}
}
static void
{
unsigned int i;
/* remove extension updates */
/* remove keywords */
if (array_is_created(&t->keyword_updates)) {
seq);
}
seq);
}
}
}
/* remove modseqs */
if (array_is_created(&t->modseq_updates) &&
/* and finally remove the append itself */
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);
}
}
{
static guid_128_t null_guid =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
}
const guid_128_t guid_128)
{
const struct mail_transaction_expunge_guid *expunges;
struct mail_transaction_expunge_guid *expunge;
unsigned int count;
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. */
} else {
t->log_updates = TRUE;
/* ignore duplicates here. drop them when committing. */
if (!array_is_created(&t->expunges))
else if (!t->expunges_nonsorted) {
/* usually expunges are added in increasing order. */
t->expunges_nonsorted = TRUE;
}
}
}
static void update_minmax_flagupdate_seq(struct mail_index_transaction *t,
{
if (t->min_flagupdate_seq == 0) {
t->min_flagupdate_seq = seq1;
t->max_flagupdate_seq = seq2;
} else {
if (t->min_flagupdate_seq > seq1)
t->min_flagupdate_seq = seq1;
if (t->max_flagupdate_seq < seq2)
t->max_flagupdate_seq = seq2;
}
}
unsigned int
unsigned int left_idx,
unsigned int right_idx,
{
const struct mail_index_flag_update *updates;
/* find the first update with either overlapping range,
or the update which will come after our insert */
else
break;
}
idx++;
return idx;
}
static void
struct mail_index_flag_update u,
unsigned int idx)
{
/* first we'll just add the changes without trying to merge anything */
/* insert new update */
tmp_update = u;
} else {
/* split existing update from beginning */
}
idx++;
}
/* split existing update from end */
}
~u.remove_flags;
~u.add_flags;
/* we can remove this update completely */
}
/* break here before idx++ so last_update_idx is set
correctly */
break;
}
}
}
/* merge everything */
/* merge */
max--;
if (t->last_update_idx > idx)
t->last_update_idx--;
} else {
idx++;
}
}
}
enum modify_type modify_type,
enum mail_flags flags)
{
switch (modify_type) {
case MODIFY_REPLACE:
break;
case MODIFY_ADD:
break;
case MODIFY_REMOVE:
break;
}
}
void mail_index_update_flags_range(struct mail_index_transaction *t,
enum modify_type modify_type,
enum mail_flags flags)
{
struct mail_index_record *rec;
struct mail_index_flag_update u, *last_update;
if (seq2 >= t->first_new_seq) {
/* updates for appended messages, modify them directly */
}
if (seq1 >= t->first_new_seq)
return;
/* range contains also existing messages. update them next. */
}
if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES) != 0)
memset(&u, 0, sizeof(u));
switch (modify_type) {
case MODIFY_REPLACE:
break;
case MODIFY_ADD:
if (flags == 0)
return;
break;
case MODIFY_REMOVE:
if (flags == 0)
return;
u.remove_flags = flags;
break;
}
if (!array_is_created(&t->updates)) {
return;
}
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;
/* we can just update the UID range */
return;
}
/* hopefully we can just append it */
t->last_update_idx++;
last_update++;
}
}
if (t->last_update_idx == count)
else {
/* slow path */
/* added after this */
} else {
/* added before this or on top of this */
first_idx = 0;
}
mail_index_insert_flag_update(t, u, idx);
}
}
enum modify_type modify_type,
enum mail_flags flags)
{
}
void mail_index_update_header(struct mail_index_transaction *t,
bool prepend)
{
t->log_updates = TRUE;
if (prepend) {
t->pre_hdr_changed = TRUE;
} else {
t->post_hdr_changed = TRUE;
}
}
{
struct mail_transaction_ext_intro intro;
/* get ext_id from transaction's map if it's there */
/* have to create it */
const struct mail_index_registered_ext *rext;
} else {
const struct mail_index_ext *ext;
}
/* allow only header size changes if extension records have already
been changed in transaction */
(old_record_size == record_size &&
old_record_align == record_align));
t->log_ext_updates = TRUE;
if (!array_is_created(&t->ext_resizes))
}
{
struct mail_transaction_ext_reset reset;
if (!array_is_created(&t->ext_resets))
t->log_ext_updates = TRUE;
}
{
if (!array_is_created(&t->ext_reset_atomic))
}
static bool
{
if (array_is_created(arr)) {
if (array_is_created(array))
return TRUE;
}
}
return FALSE;
}
static bool
{
return TRUE;
return TRUE;
if (array_is_created(&t->ext_hdr_updates)) {
const struct mail_index_transaction_ext_hdr_update *hdr;
if (hdr->alloc_size > 0)
return TRUE;
}
}
if (array_is_created(&t->ext_resets)) {
const struct mail_transaction_ext_reset *reset;
if (reset->new_reset_id != 0)
return TRUE;
}
}
if (array_is_created(&t->ext_resizes)) {
const struct mail_transaction_ext_intro *resize;
return TRUE;
}
}
return FALSE;
}
static void
{
/* if extension records have been updated, clear them */
if (array_is_created(array))
}
}
static void
{
if (array_is_created(&t->ext_hdr_updates) &&
/* if extension headers have been updated, clear them */
struct mail_index_transaction_ext_hdr_update *hdr;
if (hdr->alloc_size > 0) {
}
hdr->alloc_size = 0;
}
if (array_is_created(&t->ext_resets) &&
/* clear resets */
}
if (array_is_created(&t->ext_resizes) &&
/* clear resizes */
}
}
void mail_index_ext_using_reset_id(struct mail_index_transaction *t,
{
bool changed;
if (!array_is_created(&t->ext_reset_ids))
*reset_id_p = reset_id;
if (changed) {
/* reset_id changed, clear existing changes */
}
}
void mail_index_ext_set_reset_id(struct mail_index_transaction *t,
{
/* make sure the changes get reset, even if reset_id doesn't change */
}
void mail_index_update_header_ext(struct mail_index_transaction *t,
{
struct mail_index_transaction_ext_hdr_update *hdr;
if (!array_is_created(&t->ext_hdr_updates))
}
t->log_ext_updates = TRUE;
}
{
const struct mail_index_registered_ext *rext;
const struct mail_transaction_ext_intro *intro;
unsigned int count;
seq <= t->last_new_seq));
t->log_ext_updates = TRUE;
if (!array_is_created(&t->ext_resizes)) {
count = 0;
} else {
}
/* resized record */
} else {
}
if (!array_is_created(&t->ext_rec_updates))
/* @UNSAFE */
old_data_r)) {
/* not found, clear old_data if it was given */
if (old_data_r != NULL)
}
}
int mail_index_atomic_inc_ext(struct mail_index_transaction *t,
{
seq <= t->last_new_seq));
/* currently non-external transactions can be applied multiple times,
causing multiple increments. FIXME: we need this now and it doesn't
actually seem to be a real problem at least right now - why? */
/*i_assert((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0);*/
t->log_ext_updates = TRUE;
if (!array_is_created(&t->ext_rec_atomics))
&old_diff32)) {
/* already incremented this sequence in this transaction */
diff32 += old_diff32;
}
return diff32;
}
static bool
enum modify_type modify_type,
struct mail_keywords *keywords)
{
const unsigned int *existing_idx;
unsigned int i, j, existing_count;
bool found;
if (seq < t->first_new_seq)
return TRUE;
for (j = 0; j < existing_count; j++) {
break;
}
}
switch (modify_type) {
case MODIFY_ADD:
case MODIFY_REPLACE:
if (!found)
return TRUE;
break;
case MODIFY_REMOVE:
if (found)
return TRUE;
break;
}
}
return FALSE;
}
static struct mail_keywords *
{
/* syncing is saving a list of changes into this transaction.
the seq is actual an uid, so we can't lookup the existing
keywords. we shouldn't get here unless we're reading
pre-v2.2 keyword-reset records from .log files. so we don't
really care about performance that much here, */
for (i = 0; i < keywords_count; i++)
} else {
}
if (array_count(&keywords) == 0)
return NULL;
&keywords);
}
enum modify_type modify_type,
struct mail_keywords *keywords)
{
struct mail_index_transaction_keyword_update *u;
unsigned int i;
bool changed;
seq <= t->last_new_seq));
if (!array_is_created(&t->keyword_updates)) {
}
if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES) != 0) {
T_BEGIN {
keywords);
} T_END;
if (!changed)
return;
}
switch (modify_type) {
case MODIFY_REPLACE:
/* split this into add+remove. remove all existing keywords not
included in the keywords list */
if (seq < t->first_new_seq) {
/* remove the ones currently in index */
}
/* remove from all changes we've done in this transaction */
break;
case MODIFY_ADD:
break;
case MODIFY_REMOVE:
break;
}
/* Update add_seq and remove_seq arrays which describe the keyword
changes. First do the removes, since replace removes everything
first. */
if (remove_keywords != NULL) {
for (i = 0; i < remove_keywords->count; i++) {
u = array_idx_modifiable(&t->keyword_updates,
remove_keywords->idx[i]);
/* Don't bother updating remove_seq for new messages,
since their initial state is "no keyword" anyway */
if (seq < t->first_new_seq) {
16, seq);
}
}
}
if (add_keywords != NULL) {
for (i = 0; i < add_keywords->count; i++) {
u = array_idx_modifiable(&t->keyword_updates,
add_keywords->idx[i]);
}
}
t->log_updates = TRUE;
}
bool mail_index_cancel_flag_updates(struct mail_index_transaction *t,
{
unsigned int i, count;
if (!array_is_created(&t->updates))
return FALSE;
if (i == count)
return FALSE;
else {
return FALSE;
}
/* exists */
else if (count > 1)
else
array_free(&t->updates);
} else {
/* need to split it in two */
tmp_update = updates[i];
}
return TRUE;
}
{
if (array_is_created(array)) {
if (array_count(array) == 0)
return TRUE;
}
}
return FALSE;
}
bool mail_index_cancel_keyword_updates(struct mail_index_transaction *t,
{
struct mail_index_transaction_keyword_update *kw;
if (!array_is_created(&t->keyword_updates))
return FALSE;
}
if (!have_kw_changes)
array_free(&t->keyword_updates);
return ret;
}
void mail_index_transaction_reset(struct mail_index_transaction *t)
{
t->v.reset(t);
}
void mail_index_reset(struct mail_index_transaction *t)
{
}
void mail_index_set_deleted(struct mail_index_transaction *t)
{
i_assert(!t->index_undeleted);
t->index_deleted = TRUE;
}
void mail_index_set_undeleted(struct mail_index_transaction *t)
{
i_assert(!t->index_deleted);
t->index_undeleted = TRUE;
}
void mail_index_transaction_set_max_modseq(struct mail_index_transaction *t,
{
t->max_modseq = max_modseq;
t->conflict_seqs = seqs;
}