From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 278F41FBC9 for ; Wed, 10 Jun 2020 07:07:29 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 53/82] imap: allow UID range search on timestamps Date: Wed, 10 Jun 2020 07:04:50 +0000 Message-Id: <20200610070519.18252-54-e@yhbt.net> In-Reply-To: <20200610070519.18252-1-e@yhbt.net> References: <20200610070519.18252-1-e@yhbt.net> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: Since it seems somewhat common for IMAP clients to limit searches by sent Date: or INTERNALDATE, we can rely on the NNTP/WWW-optimized overview DB. For other queries, we'll have to depend on the Xapian DB. --- lib/PublicInbox/DummyInbox.pm | 2 +- lib/PublicInbox/IMAP.pm | 13 ++++++++----- lib/PublicInbox/Over.pm | 13 +++++++++++++ t/imapd.t | 6 ++++++ 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/PublicInbox/DummyInbox.pm b/lib/PublicInbox/DummyInbox.pm index e38f9e5a432..b6c48db1a0e 100644 --- a/lib/PublicInbox/DummyInbox.pm +++ b/lib/PublicInbox/DummyInbox.pm @@ -13,7 +13,7 @@ sub max { undef } # Msgmap::max sub msg_range { [] } # Msgmap::msg_range no warnings 'once'; -*query_xover = \&msg_range; +*uid_range = *query_xover = \&msg_range; *over = \&mm; *subscribe_unlock = *unsubscribe_unlock = *get_art = *description = *base_url = \&max; diff --git a/lib/PublicInbox/IMAP.pm b/lib/PublicInbox/IMAP.pm index f9af530aa19..78042b9e2ee 100644 --- a/lib/PublicInbox/IMAP.pm +++ b/lib/PublicInbox/IMAP.pm @@ -811,10 +811,11 @@ sub parse_date ($) { # 02-Oct-1993 } sub uid_search_uid_range { # long_response - my ($self, $tag, $beg, $end) = @_; - my $uids = $self->{ibx}->mm->msg_range($beg, $end, 'num'); + my ($self, $tag, $beg, $end, $sql) = @_; + my $uids = $self->{ibx}->over->uid_range($$beg, $end, $sql); if (@$uids) { - $self->msg_more(join('', map { " $_->[0]" } @$uids)); + $$beg = $uids->[-1] + 1; + $self->msg_more(join(' ', '', @$uids)); } else { $self->write(\"\r\n$tag OK Search done\r\n"); undef; @@ -936,13 +937,15 @@ sub cmd_uid_search ($$$;) { my $ibx = $self->{ibx} or return "$tag BAD No mailbox selected\r\n"; my $q = parse_query($self, \@_); return "$tag $q\r\n" if !ref($q); + my $sql = delete $q->{sql}; if (!scalar(keys %$q)) { $self->msg_more('* SEARCH'); my $beg = $self->{uid_min} // 1; my $end = $ibx->mm->max; uid_clamp($self, \$beg, \$end); - long_response($self, \&uid_search_uid_range, $tag, \$beg, $end); + long_response($self, \&uid_search_uid_range, + $tag, \$beg, $end, $sql); } elsif (my $uid = $q->{uid}) { if ($uid =~ /\A([0-9]+):([0-9]+|\*)\z/s) { my ($beg, $end) = ($1, $2); @@ -950,7 +953,7 @@ sub cmd_uid_search ($$$;) { uid_clamp($self, \$beg, \$end); $self->msg_more('* SEARCH'); long_response($self, \&uid_search_uid_range, - $tag, \$beg, $end); + $tag, \$beg, $end, $sql); } elsif ($uid =~ /\A[0-9]+\z/s) { $uid = $ibx->over->get_art($uid) ? " $uid" : ''; "* SEARCH$uid\r\n$tag OK Search done\r\n"; diff --git a/lib/PublicInbox/Over.pm b/lib/PublicInbox/Over.pm index 286fb7f6b4f..402cbf7ce07 100644 --- a/lib/PublicInbox/Over.pm +++ b/lib/PublicInbox/Over.pm @@ -215,4 +215,17 @@ SELECT num,ts,ds,ddd FROM over WHERE num = ? LIMIT 1 load_from_row($smsg); } +# IMAP search +sub uid_range { + my ($self, $beg, $end, $sql) = @_; + my $dbh = $self->connect; + my $q = 'SELECT num FROM over WHERE num >= ? AND num <= ?'; + + # This is read-only, anyways; but caller should verify it's + # only sending \A[0-9]+\z for ds and ts column ranges + $q .= $$sql if $sql; + $q .= ' ORDER BY num ASC LIMIT ' . DEFAULT_LIMIT; + $dbh->selectcol_arrayref($q, undef, $beg, $end); +} + 1; diff --git a/t/imapd.t b/t/imapd.t index 11b56b09dbe..2546ab51ac6 100644 --- a/t/imapd.t +++ b/t/imapd.t @@ -326,6 +326,12 @@ Content-Type: message/rfc822\r Content-Disposition: attachment; filename="embed2x\.eml"\r \r EOF + + my @hits = $mic->search('SENTON' => '18-Apr-2020'); + is_deeply(\@hits, [ $uidnext ], 'search with date condition works'); + ok($mic->examine($ng), 'EXAMINE on dummy'); + @hits = $mic->search('SENTSINCE' => '18-Apr-2020'); + is_deeply(\@hits, [], 'search on dummy with condition works'); }); # each_inbox # message sequence numbers :<