mail-index-transaction.c revision 19f2cdd797b1ed206eb007bd245e857a7e68c2fc
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Inside transaction we keep messages stored in sequences in uid fields.
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen Before they're written to transaction log the sequences are changed to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen UIDs. This is because we're able to compress sequence ranges better. */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenvoid mail_index_transaction_reset(struct mail_index_transaction *t)
fa5957ffc9b676bfd649fa9953e63e72ee4ebeb4Timo Sirainen struct mail_index_transaction_ext_hdr_update **ext_hdrs;
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen recs = array_get_modifiable(&t->ext_rec_updates, &count);
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen for (i = 0; i < count; i++) {
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen ext_hdrs = array_get_modifiable(&t->ext_hdr_updates, &count);
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen for (i = 0; i < count; i++)
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen struct mail_index_transaction_keyword_update *u;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen u = array_get_modifiable(&t->keyword_updates, &count);
03739a8eaad2d8b34b9d87dbbe5b13c5d5dfa11aTimo Sirainen for (i = 0; i < count; i++) {
c3b36993785d9567a63bdad1933031060c9460cfTimo Sirainen t->first_new_seq = mail_index_view_get_messages_count(t->view)+1;
c3b36993785d9567a63bdad1933031060c9460cfTimo Sirainen memset(t->pre_hdr_mask, 0, sizeof(t->pre_hdr_mask));
c3b36993785d9567a63bdad1933031060c9460cfTimo Sirainen memset(t->post_hdr_mask, 0, sizeof(t->post_hdr_mask));
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen mail_cache_transaction_rollback(t->cache_trans_ctx);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void mail_index_transaction_free(struct mail_index_transaction *t)
eaa36e8058dd6ca4f729f4d0667f8be9912ab473Timo Sirainenmail_index_transaction_get_view(struct mail_index_transaction *t)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenbool mail_index_transaction_is_expunged(struct mail_index_transaction *t,
c3b36993785d9567a63bdad1933031060c9460cfTimo Sirainenvoid mail_index_transaction_ref(struct mail_index_transaction *t)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_index_transaction_unref(struct mail_index_transaction **_t)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (--t->refcount == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenbool mail_index_seq_array_lookup(const ARRAY_TYPE(seq_array) *array,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we're probably appending it, check */
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainenstatic bool mail_index_seq_array_add(ARRAY_TYPE(seq_array) *array, uint32_t seq,
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen /* records need to be 32bit aligned */
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen i_assert(array->arr.element_size == sizeof(seq) + aligned_record_size);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen if (mail_index_seq_array_lookup(array, seq, &idx)) {
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen /* already there, update */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* save the old record before overwriting it */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen memcpy(old_record, PTR_OFFSET(p, sizeof(seq)),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memcpy(PTR_OFFSET(p, sizeof(seq)), record, record_size);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen memcpy(PTR_OFFSET(p, sizeof(seq)), record, record_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_index_buffer_convert_to_uids(struct mail_index_transaction *t,
06fb99af33bd380b382d2d4f2994cf9a5bf0bbaeTimo Sirainen for (i = 0; i < count; i++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rec = MAIL_INDEX_MAP_IDX(view->map, *seq - 1);
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen /* we're using only rec->uid, no need to bother locking
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen the index. */
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen /* FIXME: replace with simple assert once we
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen figure out why this happens.. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "first_new_seq = %u, records = %u",
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainenstatic void keyword_updates_convert_to_uids(struct mail_index_transaction *t)
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen struct mail_index_transaction_keyword_update *updates;
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen unsigned int i, count;
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen updates = array_get_modifiable(&t->keyword_updates, &count);
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen for (i = 0; i < count; i++) {
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen if (array_is_created(&updates[i].remove_seq)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_index_transaction_convert_to_uids(struct mail_index_transaction *t)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int i, count;
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen updates = array_get_modifiable(&t->ext_rec_updates, &count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (i = 0; i < count; i++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_buffer_convert_to_uids(t, &updates[i],
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen mail_index_buffer_convert_to_uids(t, (void *)&t->expunges, TRUE);
6d25922a089626f5535d51358e33d3337783a410Timo Sirainen mail_index_buffer_convert_to_uids(t, (void *)&t->updates, TRUE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_buffer_convert_to_uids(t, (void *)&t->keyword_resets, TRUE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int uid_map_cmp(const void *p1, const void *p2)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_index_update_day_headers(struct mail_index_transaction *t)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sizeof(hdr.day_first_uid) / sizeof(hdr.day_first_uid[0]);
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen /* get beginning of today */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* get number of days since last message */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* @UNSAFE: move days forward and fill the missing days with old
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen day_first_uid[0]. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memcpy(hdr.day_first_uid + days, hdr.day_first_uid, max_days - days);
06a5ffa611efd86ffb9acc8beb0fe66e7bd844e4Timo Sirainen offsetof(struct mail_index_header, day_stamp),
06a5ffa611efd86ffb9acc8beb0fe66e7bd844e4Timo Sirainen &hdr.day_stamp, sizeof(hdr.day_stamp), FALSE);
06a5ffa611efd86ffb9acc8beb0fe66e7bd844e4Timo Sirainen offsetof(struct mail_index_header, day_first_uid),
06a5ffa611efd86ffb9acc8beb0fe66e7bd844e4Timo Sirainen hdr.day_first_uid, sizeof(hdr.day_first_uid), FALSE);
06a5ffa611efd86ffb9acc8beb0fe66e7bd844e4Timo Sirainenvoid mail_index_transaction_sort_appends(struct mail_index_transaction *t)
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen unsigned int i, j, count, ext_rec_array_count;
24815ed8224a0647926b49b9a1f716efb2a57148Timo Sirainen /* first make a copy of the UIDs and map them to sequences */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen recs = array_get_modifiable(&t->appends, &count);
24815ed8224a0647926b49b9a1f716efb2a57148Timo Sirainen for (i = 0; i < count; i++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* now sort the UID map */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen qsort(new_uid_map, count, sizeof(*new_uid_map), uid_map_cmp);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen for (i = 0; i < count; i++)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* sort mail records */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sorted_recs = i_new(struct mail_index_record, count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (i = 0; i < count; i++)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_write(t->appends.arr.buffer, 0, sorted_recs,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* fix the order in extensions */
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen ext_rec_arrays = array_get_modifiable(&t->ext_rec_updates,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (j = 0; j < ext_rec_array_count; j++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ARRAY_TYPE(seq_array) *old_array = &ext_rec_arrays[j];
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen for (i = 0; i < ext_count; i++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen seq = *ext_rec < t->first_new_seq ? *ext_rec :
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_seq_array_add(&new_array, seq, ext_rec+1,
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* FIXME: fix the order in keywords */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainenuint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t)
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen next_uid = t->reset ? 1 : t->view->map->hdr.next_uid;
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen /* get next_uid from appends if they have UIDs */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen /* see if it's been updated in pre/post header changes */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen offset = offsetof(struct mail_index_header, next_uid);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenstatic int _mail_index_transaction_commit(struct mail_index_transaction *t,
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen mail_cache_transaction_commit(t->cache_trans_ctx);
c3b36993785d9567a63bdad1933031060c9460cfTimo Sirainen if (mail_index_transaction_convert_to_uids(t) < 0)
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen ret = mail_transaction_log_append(t, log_file_seq_r,
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenstatic void _mail_index_transaction_rollback(struct mail_index_transaction *t)
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen mail_cache_transaction_rollback(t->cache_trans_ctx);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenint mail_index_transaction_commit(struct mail_index_transaction **_t,
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if (mail_index_view_is_inconsistent(t->view)) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen return t->v.commit(t, log_file_seq_r, log_file_offset_r);
bb2b91b4c5363348b737237893d414639510a561Timo Sirainenvoid mail_index_transaction_rollback(struct mail_index_transaction **_t)
bb2b91b4c5363348b737237893d414639510a561Timo Sirainenmail_index_transaction_lookup(struct mail_index_transaction *t, uint32_t seq)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_assert(seq >= t->first_new_seq && seq <= t->last_new_seq);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen return array_idx_modifiable(&t->appends, seq - t->first_new_seq);
bb2b91b4c5363348b737237893d414639510a561Timo Sirainenvoid mail_index_append(struct mail_index_transaction *t, uint32_t uid,
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen /* sequence number is visible only inside given view,
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen so let it generate it */
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen /* if previous record's UID is larger than this one,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen we'll have to sort the appends later */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen rec = mail_index_transaction_lookup(t, *seq_r - 1);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenvoid mail_index_append_assign_uids(struct mail_index_transaction *t,
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen unsigned int i, count;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen recs = array_get_modifiable(&t->appends, &count);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen /* find the first mail with uid = 0 */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen for (i = 0; i < count; i++) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen for (; i < count; i++) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenvoid mail_index_expunge(struct mail_index_transaction *t, uint32_t seq)
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(t->view));
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen /* expunges is a sorted array of {seq1, seq2, ..}, .. */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenmail_transaction_update_want_add(struct mail_index_transaction *t,
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES) == 0)
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (mail_index_lookup(t->view, seq, &rec) < 0)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if ((rec->flags & u->add_flags) != u->add_flags ||
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainenmail_index_insert_flag_update(struct mail_index_transaction *t,
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen struct mail_transaction_flag_update *updates, tmp_update;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen unsigned int count;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen updates = array_get_modifiable(&t->updates, &count);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen i_assert(left_idx <= right_idx && right_idx <= count);
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen /* find the first update with either overlapping range,
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen or the update which will come after our insert */
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen if (idx < count && updates[idx].uid2 < u.uid1)
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen /* overlapping ranges, split/merge them */
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen i_assert(idx == 0 || updates[idx-1].uid2 < u.uid1);
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen i_assert(idx == count || updates[idx].uid2 >= u.uid1);
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen for (; idx < count && u.uid2 >= updates[idx].uid1; idx++) {
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen updates[idx].remove_flags != u.remove_flags)) {
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen /* insert new update */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen /* split existing update from beginning */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen i_assert(updates[idx].uid1 <= updates[idx].uid2);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if (mail_transaction_update_want_add(t, &tmp_update)) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen array_insert(&t->updates, idx, &tmp_update, 1);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen updates[idx].remove_flags != u.remove_flags)) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* split existing update from end */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen i_assert(updates[idx].uid1 <= updates[idx].uid2);
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen if (mail_transaction_update_want_add(t, &tmp_update))
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen array_insert(&t->updates, idx, &tmp_update, 1);
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen updates = array_get_modifiable(&t->updates, &count);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen (updates[idx].remove_flags | u.remove_flags) &
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* we can remove this update completely */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen updates = array_get_modifiable(&t->updates, &count);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* break here before idx++ so last_update_idx is set
if (mail_transaction_update_want_add(t, &u)) {
count++;
switch (modify_type) {
case MODIFY_REPLACE:
case MODIFY_ADD:
case MODIFY_REMOVE:
memset(&u, 0, sizeof(u));
switch (modify_type) {
case MODIFY_REPLACE:
case MODIFY_ADD:
case MODIFY_REMOVE:
if (mail_transaction_update_want_add(t, &u))
t->last_update_idx++;
last_update++;
if (mail_transaction_update_want_add(t, &u))
else if (t->last_update_idx > 0)
t->last_update_idx--;
first_idx = 0;
bool prepend)
if (prepend) {
unsigned int i, count;
for (i = 0; i < count; i++) {
return TRUE;
for (i = 0; i < count; i++) {
return TRUE;
for (i = 0; i < count; i++) {
if (ids[i] != 0)
return TRUE;
for (i = 0; i < count; i++) {
return TRUE;
return FALSE;
unsigned int count;
count = 0;
old_data_r)) {
struct mail_keywords *
const char *const keywords[])
struct mail_keywords *k;
unsigned int i, count;
if (count == 0) {
for (i = 0; i < count; i++) {
struct mail_keywords *
struct mail_keywords *k;
unsigned int count;
if (count == 0) {
struct mail_index_transaction_keyword_update *u;
unsigned int i, ku_count;
switch (modify_type) {
case MODIFY_ADD:
case MODIFY_REMOVE:
case MODIFY_REPLACE:
&ku_count);
for (i = 0; i < ku_count; i++) {
struct mail_index_transaction *
struct mail_index_transaction *t;
t->v = trans_vfuncs;
t->first_new_seq =