bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
211c638d81d382517d196ad47565e0d85012c927klemens/* The idea is that we use 32bit integers for string sort IDs which specify
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen the sort order for primary sort condition. The whole 32bit integer space is
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen used and whenever adding a string, the available space is halved and the new
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen ID is added in the middle. For example if we add one mail the first time, it
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen gets ID 2^31. If we then add two mails which are sorted before the first
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen one, they get IDs 2^31/3 and 2^31/3*2. Once we run out of the available
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen space between IDs, more space is made by renumbering some IDs.
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo SirainenARRAY_DEFINE_TYPE(mail_sort_node, struct mail_sort_node);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen ARRAY_TYPE(mail_sort_node) zero_nodes, nonzero_nodes, sorted_nodes;
5a3aadf1f09af57cc8581c19234db45b00ef77fbTimo Sirainen uint32_t ext_id, last_seq, highest_reset_id, prev_seq;
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenstatic struct sort_string_context *static_zero_cmp_context;
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainenstatic void index_sort_list_reset_broken(struct sort_string_context *ctx,
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen const char *reason);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenstatic void index_sort_node_add(struct sort_string_context *ctx,
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenvoid index_sort_list_init_string(struct mail_search_sort_program *program)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen switch (program->sort_program[0] & MAIL_SORT_MASK) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen program->context = ctx = i_new(struct sort_string_context, 1);
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen ctx->reverse = (program->sort_program[0] & MAIL_SORT_FLAG_REVERSE) != 0;
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen ctx->ext_id = mail_index_ext_register(program->t->box->index, name, 0,
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainenstatic int sort_node_seq_cmp(const struct mail_sort_node *n1,
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenstatic void index_sort_generate_seqs(struct sort_string_context *ctx)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen nodes = array_get_modifiable(&ctx->nonzero_nodes, &count);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen nodes2 = array_get_modifiable(&ctx->zero_nodes, &count2);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen i_array_init(&ctx->program->seqs, count + count2);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (i = j = 0;;) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen } else if (i < count) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen } else if (j < count2) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenstatic void index_sort_reget_sort_ids(struct sort_string_context *ctx)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen seqs = array_get(&ctx->program->seqs, &count);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (i = 0; i < count; i++) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenstatic void index_sort_node_add(struct sort_string_context *ctx,
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen mail_index_lookup_ext_full(ctx->program->t->view, node->seq,
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* we don't want to update expunged messages' sort IDs */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* we can't trust expunged messages' sort IDs. they might be
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen valid, but it's also possible that sort IDs were updated
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen and the expunged messages' sort IDs became invalid. we could
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen use sort ID if we could know the extension's reset_id at the
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen time of the expunge so we could compare it to
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen highest_reset_id, but this isn't currently possible. */
08f7dfe63ae5afae1b1f68d4de108d18cdd737b0Timo Sirainen node->sort_id = ctx->broken || data == NULL ? 0 :
f6a1fbad55980c9b257ffc1881a55dc7fafe9a83Timo Sirainen if (ctx->lowest_nonexpunged_zero > node->seq ||
08f7dfe63ae5afae1b1f68d4de108d18cdd737b0Timo Sirainen } else if (ctx->lowest_nonexpunged_zero != 0 &&
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen index_sort_list_reset_broken(ctx, t_strdup_printf(
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen "sort_id=0 found in the middle "
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen "(uid=%u has sort_id, uid=%u doesn't)",
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* if reset ID increases, lookup all existing messages' sort
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen IDs again. if it decreases, ignore the sort ID. */
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen if (!mail_index_ext_get_reset_id(ctx->program->t->view, map,
2511b0c8fac94fbd3bc58f515aab22855ddf49d9Timo Sirainen /* a bit late to start changing the reset_id.
2511b0c8fac94fbd3bc58f515aab22855ddf49d9Timo Sirainen the node lists aren't ordered by sequence
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenvoid index_sort_list_add_string(struct mail_search_sort_program *program,
5a3aadf1f09af57cc8581c19234db45b00ef77fbTimo Sirainen struct sort_string_context *ctx = program->context;
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainenstatic int sort_node_zero_string_cmp(const struct mail_sort_node *n1,
3e937db1db323cc690bbadb552c17e8492589b8bTimo Sirainen struct sort_string_context *ctx = static_zero_cmp_context;
3e937db1db323cc690bbadb552c17e8492589b8bTimo Sirainen ret = strcmp(ctx->sort_strings[n1->seq], ctx->sort_strings[n2->seq]);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenstatic void index_sort_zeroes(struct sort_string_context *ctx)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen enum mail_sort_type sort_type = ctx->program->sort_program[0];
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* first get all the messages' sort strings. although this takes more
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen memory, it makes error handling easier and probably also helps
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen CPU caching. */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen ctx->sort_strings = i_new(const char *, ctx->last_seq + 1);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen pool_alloconly_create("sort strings", 1024*64);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen nodes = array_get_modifiable(&ctx->zero_nodes, &count);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (i = 0; i < count; i++) {
a85cf0bdd86ba36e6f1243e9b6848a335625185bTimo Sirainen if (index_sort_header_get(ctx->program, nodes[i].seq,
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* we have all strings, sort nodes based on them */
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen array_sort(&ctx->zero_nodes, sort_node_zero_string_cmp);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenindex_sort_get_expunged_string(struct sort_string_context *ctx, uint32_t idx,
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen enum mail_sort_type sort_type = ctx->program->sort_program[0];
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* Look forwards and backwards to see if there are
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen identical sort_ids. If we do find them, try to get
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen their sort string and use it to update the rest. */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen nodes = array_get(&ctx->nonzero_nodes, &count);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* If previous sort ID is identical and its sort string is set, we can
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen trust it. If it's expunged, we already verified that there are no
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen non-expunged messages. */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen if (idx > 0 && nodes[idx-1].sort_id == sort_id &&
6cf18519f0804755385871d84384834465b2d68eTimo Sirainen *result_r = ctx->sort_strings[nodes[idx].seq];
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* Go forwards as long as there are identical sort IDs. If we find one
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen that's not expunged, update string table for all messages with
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen identical sort IDs. */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen if (ctx->sort_strings[nodes[i].seq] != NULL) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* usually we fill all identical sort_ids and this
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen shouldn't happen, but we can get here if we skipped
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen over messages when binary searching */
a85cf0bdd86ba36e6f1243e9b6848a335625185bTimo Sirainen if (index_sort_header_get(ctx->program, nodes[i].seq,
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* unknown */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* fill all identical sort_ids with the same value */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (i = idx; i > 0 && nodes[i-1].sort_id == sort_id; i--) ;
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (i = idx; i < count && nodes[i].sort_id == sort_id; i++)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenindex_sort_get_string(struct sort_string_context *ctx,
1a229e3e15ed387208c13e7dfe56984becf90288Timo Sirainen const char **str_r)
0a6a527f0c42b5478d80ac53ab357885676fd516Timo Sirainen /* we've already determined that we can't do this lookup */
a85cf0bdd86ba36e6f1243e9b6848a335625185bTimo Sirainen ret = index_sort_header_get(ctx->program, seq,
6cf18519f0804755385871d84384834465b2d68eTimo Sirainen else if (ret == 0) {
6cf18519f0804755385871d84384834465b2d68eTimo Sirainen if (!index_sort_get_expunged_string(ctx, idx, str, &result))
6cf18519f0804755385871d84384834465b2d68eTimo Sirainen /* found the expunged string - return success */
6cf18519f0804755385871d84384834465b2d68eTimo Sirainen ctx->sort_strings[seq] = str_len(str) == 0 ? "" :
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenindex_sort_bsearch(struct sort_string_context *ctx, const char *key,
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen nodes = array_get_modifiable(&ctx->nonzero_nodes, &right_idx);
91ede77590440f8613c477bac1b7336ca8928e87Timo Sirainen if (index_sort_get_string(ctx, idx, &nodes[idx], &str))
6cf18519f0804755385871d84384834465b2d68eTimo Sirainen /* put expunged (and otherwise failed) messages first */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen else if (ret < 0)
a9ce43c494ce7434277f6b30e591f4b832e2d49dTimo Sirainen nodes[prev-1].sort_id == nodes[prev].sort_id);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenstatic void index_sort_merge(struct sort_string_context *ctx)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen unsigned int zpos, nzpos, nz_next_pos, zcount, nzcount;
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* both zero_nodes and nonzero_nodes are sorted. we'll now just have
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen to merge them together. use sorted_nodes as the result array. */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen i_array_init(&ctx->sorted_nodes, array_count(&ctx->nonzero_nodes) +
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen znodes = array_get_modifiable(&ctx->zero_nodes, &zcount);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen nznodes = array_get_modifiable(&ctx->nonzero_nodes, &nzcount);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (zpos = nzpos = 0; zpos < zcount && nzpos < nzcount; ) {
91ede77590440f8613c477bac1b7336ca8928e87Timo Sirainen if (index_sort_get_string(ctx, nzpos, &nznodes[nzpos], &nzstr))
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen else if (prev_str != NULL && strcmp(zstr, prev_str) == 0) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* identical to previous message, must keep them
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* we can't be yet sure about the order, but future
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen nznodes may reveal that the znode must be added
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen later. if future nznodes don't reveal that, we have
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen no idea about these nodes' order. so just always
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen put the expunged message first. */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen array_append(&ctx->sorted_nodes, &znodes[zpos], 1);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen array_append(&ctx->sorted_nodes, &nznodes[nzpos], 1);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* avoid looking up all existing messages' strings by
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen binary searching the next zero-node position. don't
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen bother if it looks like more work than linear
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* only one of zero_nodes and nonzero_nodes can be non-empty now */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen array_append(&ctx->sorted_nodes, &znodes[zpos], 1);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen array_append(&ctx->sorted_nodes, &nznodes[nzpos], 1);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* future index_sort_get_string() calls use ctx->nonzero_nodes, but we
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen use only ctx->sorted_nodes. make them identical. */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenindex_sort_add_ids_range(struct sort_string_context *ctx,
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen unsigned int left_idx, unsigned int right_idx,
d50f5a6963f56c587b1d47119c3af4da2fde45edTimo Sirainen const char *left_str = NULL, *right_str = NULL, *str = NULL;
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen uint32_t left_sort_id, right_sort_id, diff, left_str_idx = 0;
00e3803ee6a7e56f1b58bcf0452ccbfde6a22fcaTimo Sirainen bool no_left_str = FALSE, no_right_str = FALSE;
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen nodes = array_get_modifiable(&ctx->sorted_nodes, &count);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* get the sort IDs from left and right */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* check if all of them should have the same sort IDs. we don't want
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen to hit the renumbering code in that situation. */
782e7c6d33ade2d3b8a2126801c06956358abf66Timo Sirainen if (left_sort_id == right_sort_id && left_sort_id != 0) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* they should all have the same sort ID */
00e3803ee6a7e56f1b58bcf0452ccbfde6a22fcaTimo Sirainen while (diff / (right_idx-left_idx + 2) == 0) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* we most likely don't have enough space. we have to
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen renumber some of the existing sort IDs. do this by
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen widening the area we're giving sort IDs. */
b0aaaad8427cacdb9486b4406198c3301569422cTimo Sirainen if (nodes[--left_idx].sort_id != left_sort_id) {
00e3803ee6a7e56f1b58bcf0452ccbfde6a22fcaTimo Sirainen /* we did nothing, but there's still not enough space.
00e3803ee6a7e56f1b58bcf0452ccbfde6a22fcaTimo Sirainen have to renumber the leftmost/rightmost node(s) */
00e3803ee6a7e56f1b58bcf0452ccbfde6a22fcaTimo Sirainen i_assert(left_idx == 0 && right_idx == rightmost_idx);
00e3803ee6a7e56f1b58bcf0452ccbfde6a22fcaTimo Sirainen if (nodes[left_idx].sort_id != 0 && !no_left_str) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* not equivalent with any message */
00e3803ee6a7e56f1b58bcf0452ccbfde6a22fcaTimo Sirainen if (nodes[right_idx].sort_id != 0 && !no_right_str) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* not equivalent with any message */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* give (new) sort IDs to all nodes in left_idx..right_idx range.
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen divide the available space so that each message gets an equal sized
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen share. some messages' sort strings may be equivalent, so give them
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen the same sort IDs. */
91ede77590440f8613c477bac1b7336ca8928e87Timo Sirainen if (!index_sort_get_string(ctx, i, &nodes[i], &str)) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* it doesn't really matter what we give to this
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen message, since it's only temporary and we don't
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen know its correct position anyway. so let's assume
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen it's equivalent to previous message. */
c655b40d5eaeb8c1e3af0c006b29b1aed40ebb0dTimo Sirainen ret = left_str == NULL ? 1 : strcmp(str, left_str);
c655b40d5eaeb8c1e3af0c006b29b1aed40ebb0dTimo Sirainen /* broken sort_ids */
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen "(idx=%u, seq=%u, uid=%u) '%s' < left string (idx=%u, seq=%u, uid=%u) '%s'",
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen left_str_idx, nodes[left_str_idx].seq, left_str_uid, left_str);
c655b40d5eaeb8c1e3af0c006b29b1aed40ebb0dTimo Sirainen } else if (right_str != NULL && strcmp(str, right_str) == 0) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* the rest of the sort IDs should be the same */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* divide the available space equally. leave the same
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen sized space also between the first and the last
1aba6c7fba4ee28d3e90e0885424abe6a4b08e91Timo Sirainen /* broken sort IDs (we previously assigned
1aba6c7fba4ee28d3e90e0885424abe6a4b08e91Timo Sirainen left_sort_id=right_sort_id) */
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen if (right_str == NULL || strcmp(str, right_str) < 0 ||
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen *reason_r = t_strdup_printf("Invalid sort_id order ('%s' > '%s')",
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainenindex_sort_add_sort_ids(struct sort_string_context *ctx, const char **reason_r)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen nodes = array_get(&ctx->sorted_nodes, &count);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (i = 0; i < count; i++) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* get the range for all sort_id=0 nodes. include the nodes
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen left and right of the range as well */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (right_idx = i + 1; right_idx < count; right_idx++) {
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen if (index_sort_add_ids_range(ctx, left_idx, right_idx, reason_r) < 0)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenstatic void index_sort_write_changed_sort_ids(struct sort_string_context *ctx)
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen struct mail_index_transaction *itrans = ctx->program->t->itrans;
2511b0c8fac94fbd3bc58f515aab22855ddf49d9Timo Sirainen /* our reset_id is already stale - don't even bother
2511b0c8fac94fbd3bc58f515aab22855ddf49d9Timo Sirainen trying to write */
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen /* We require that there aren't sort_id=0 gaps in the middle of the
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen mails. At this point they could exist though, because some of the
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen mail lookups may have failed. Failures due to expunges don't matter,
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen because on the next lookup those mails will be lost anyway.
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen Otherwise, make sure we don't write those gaps out
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen First find the lowest non-expunged mail that has no_update set. */
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen nodes = array_get_modifiable(&ctx->sorted_nodes, &count);
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen for (i = 0; i < count; i++) {
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen if (nodes[i].no_update && lowest_failed_seq > seq &&
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen !mail_index_is_expunged(ctx->program->t->view, seq))
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen /* add the missing sort IDs to index, but only for those sequences
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen that are below lowest_failed_seq */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen nodes = array_get_modifiable(&ctx->sorted_nodes, &count);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (i = 0; i < count; i++) {
6ffa4fc008d510a503acb6f06ef847ecf84b7f93Timo Sirainen if (!nodes[i].sort_id_changed || nodes[i].no_update ||
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen mail_index_update_ext(itrans, nodes[i].seq, ext_id,
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainenstatic int sort_node_cmp(const struct mail_sort_node *n1,
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen struct sort_string_context *ctx = static_zero_cmp_context;
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenstatic void index_sort_add_missing(struct sort_string_context *ctx)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen seqs = array_get(&ctx->program->seqs, &count);
f6a1fbad55980c9b257ffc1881a55dc7fafe9a83Timo Sirainen /* we're handling only expunged zeros. if it causes us to
f6a1fbad55980c9b257ffc1881a55dc7fafe9a83Timo Sirainen renumber some existing sort IDs, don't save them. */
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainenstatic void index_sort_list_reset_broken(struct sort_string_context *ctx,
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(box, "Broken %s indexes, resetting: %s",
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen array_foreach_modifiable(&ctx->zero_nodes, node)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainenvoid index_sort_list_finish_string(struct mail_search_sort_program *program)
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen struct sort_string_context *ctx = program->context;
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* fast path: we have all sort IDs */
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen array_sort(&ctx->nonzero_nodes, sort_node_cmp);
9c3eebe94a39f13bda04926b168f54056fca05a4Timo Sirainen nodes = array_get(&ctx->nonzero_nodes, &count);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (i = 0; i < count; i++) {
5a3aadf1f09af57cc8581c19234db45b00ef77fbTimo Sirainen /* the nodes need to be sorted by sequence initially */
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen array_sort(&ctx->zero_nodes, sort_node_seq_cmp);
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen array_sort(&ctx->nonzero_nodes, sort_node_seq_cmp);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* we have to add some sort IDs. we'll do this for all
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen messages, so first remember what messages we wanted
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen to know about. */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* add messages not in seqs list */
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* sort all messages with sort IDs */
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen array_sort(&ctx->nonzero_nodes, sort_node_cmp);
c655b40d5eaeb8c1e3af0c006b29b1aed40ebb0dTimo Sirainen /* sort all messages without sort IDs */
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen /* sort lists are descending currently, but
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen merging and sort ID assigning works only
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen with ascending lists. reverse the lists
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen temporarily. we can't do this while earlier
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen because secondary sort conditions must not
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen be reversed in results (but while assigning
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen sort IDs it doesn't matter). */
c655b40d5eaeb8c1e3af0c006b29b1aed40ebb0dTimo Sirainen /* merge zero and non-zero arrays into sorted_nodes */
c655b40d5eaeb8c1e3af0c006b29b1aed40ebb0dTimo Sirainen /* give sort IDs to messages missing them */
96c7d2a59593b4ad2b4d5ec65ba5c1394e0d1171Timo Sirainen if (index_sort_add_sort_ids(ctx, &reason) == 0)
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen /* broken, try again with sort IDs reset */
0ec26d0ba45ecb400e35d672d2a97a160527bd79Timo Sirainen /* restore the correct sort order */
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen nodes = array_get(&ctx->sorted_nodes, &count);
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen for (i = 0; i < count; i++) {
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen /* NOTE: we already freed nonzero_nodes and made it point to
ed336ce8060d7dc1e452ecb0bbe923ece25c3aa8Timo Sirainen sorted_nodes. */