Skip to content

Commit

Permalink
IMAP SEARCH PARTIAL: use previous params/results to inform next one
Browse files Browse the repository at this point in the history
  • Loading branch information
ksmurchison committed Dec 6, 2023
1 parent 9bd2257 commit c892385
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 19 deletions.
13 changes: 13 additions & 0 deletions cassandane/Cassandane/Cyrus/Search.pm
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,19 @@ sub test_partial
$self->assert_str_equals('3:4', $results[0][2][0]);
$self->assert_str_equals('4:5', $results[0][2][1]);

# flag the last message
$imaptalk->store('10', '+FLAGS', '(\\flagged)');
$self->assert_str_equals('ok', $imaptalk->get_last_completion_response());

# search and return next 2 messages
@results = ();
$res = $imaptalk->_imap_cmd('SEARCH', 0, \%handlers,
'RETURN', '(PARTIAL 5:6)', 'UNDELETED');
$self->assert_str_equals('ok', $imaptalk->get_last_completion_response());
$self->assert_str_equals('PARTIAL', $results[0][1]);
$self->assert_str_equals('5:6', $results[0][2][0]);
$self->assert_str_equals('7:8', $results[0][2][1]);

# search and return last 2 messages
@results = ();
$res = $imaptalk->_imap_cmd('SEARCH', 0, \%handlers,
Expand Down
6 changes: 6 additions & 0 deletions imap/index.c
Original file line number Diff line number Diff line change
Expand Up @@ -2203,6 +2203,12 @@ EXPORTED int index_search(struct index_state *state,
sign, searchargs->partial.low,
sign, searchargs->partial.high,
seqstr ? seqstr : "NIL");

/* Save search params/results for subsequent PARTIAL */
memcpy(&state->last_search.partial,
&searchargs->partial, sizeof(range_t));
state->last_search.last_match = folder->esearch.last_match;
state->last_search.highestmodseq = state->highestmodseq;
}

free(seqstr);
Expand Down
7 changes: 6 additions & 1 deletion imap/index.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,12 @@ struct index_state {
int want_expunged;
unsigned num_expunged;
message_t *m;
seqset_t *searchres; /* RFC 5182 search results */
seqset_t *searchres; /* RFC 5182 SEARCH results */
struct {
range_t partial;
uint32_t last_match;
uint64_t highestmodseq; /* of the folder */
} last_search; /* RFC 9394 last SEARCH PARTIAL (for optimization) */
};

struct copyargs {
Expand Down
41 changes: 23 additions & 18 deletions imap/search_query.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,10 +496,10 @@ static int _subquery_run_one_folder(search_query_t *query,
{
struct searchargs *searchargs = query->searchargs;
struct index_state *state = NULL;
unsigned msgno;
unsigned msgno = 1;
unsigned nmsgs = 0;
unsigned *msgno_list = NULL;
int inc, do_all = 1;
int inc = 1, do_all = 1;
int r = 0;

if (query->sortcrit && query->verbose) {
Expand Down Expand Up @@ -540,20 +540,26 @@ static int _subquery_run_one_folder(search_query_t *query,

case SEARCH_RETURN_PARTIAL:
do_all = 0;

/* Reverse the iterator? */
if (searchargs->partial.is_last) {
msgno = state->exists;
inc = -1;
}

/* Can we use the last PARTIAL params/results to inform this one? */
if (query->state->last_search.highestmodseq == state->highestmodseq &&
query->state->last_search.partial.is_last == searchargs->partial.is_last &&
query->state->last_search.partial.high < searchargs->partial.low) {

msgno = query->state->last_search.last_match + inc;
nmsgs = query->state->last_search.partial.high;
}
break;

default:
if (!searchargs->partial.high) searchargs->partial.high = ULONG_MAX;
break;
}

if (searchargs->partial.is_last) {
msgno = state->exists;
inc = -1;
}
else {
msgno = 1;
inc = 1;
break;
}

/* One pass through the folder's message list */
Expand Down Expand Up @@ -619,23 +625,22 @@ static int _subquery_run_one_folder(search_query_t *query,
}

/* don't add anything outside of the partial range */
if (folder->esearch.all_count < searchargs->partial.low) continue;
if (folder->esearch.all_count > searchargs->partial.high) continue;
if (++nmsgs < searchargs->partial.low) continue;
if (nmsgs > searchargs->partial.high) continue;

folder_add_uid(&folder->uids, im->uid);
folder_add_modseq(folder, im->modseq);
folder->esearch.last_match = msgno;

if (query->sortcrit)
msgno_list[nmsgs] = msgno;
msgno_list[folder->esearch.uid_count] = msgno;

++nmsgs;
folder->esearch.uid_count++;
}

if (query->sortcrit && nmsgs)
query_load_msgdata(query, folder, state, msgno_list, nmsgs);

if (folder) folder->esearch.uid_count = nmsgs;

out:
if (state) query_end_index(query, &state);
free(msgno_list);
Expand Down
1 change: 1 addition & 0 deletions imap/search_query.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ struct search_folder {
struct {
/* RFC 4731 result items */
bitvector_t all_uids; /* for SAVE + PARTIAL + COUNT */
uint32_t last_match; /* msgno of last match (for next PARTIAL) */
uint32_t all_count; /* for COUNT */
uint32_t uid_count; /* of returned messages */
uint32_t min_uid; /* for MIN */
Expand Down

0 comments on commit c892385

Please sign in to comment.