LCOV - code coverage report
Current view: top level - backends/remote - remote-database.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 7822d31adece Lines: 380 425 89.4 %
Date: 2019-05-23 11:15:29 Functions: 50 53 94.3 %
Branches: 327 676 48.4 %

           Branch data     Line data    Source code
       1                 :            : /** @file remote-database.cc
       2                 :            :  *  @brief Remote backend database class
       3                 :            :  */
       4                 :            : /* Copyright (C) 2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2017,2018,2019 Olly Betts
       5                 :            :  * Copyright (C) 2007,2009,2010 Lemur Consulting Ltd
       6                 :            :  *
       7                 :            :  * This program is free software; you can redistribute it and/or
       8                 :            :  * modify it under the terms of the GNU General Public License as
       9                 :            :  * published by the Free Software Foundation; either version 2 of the
      10                 :            :  * License, or (at your option) any later version.
      11                 :            :  *
      12                 :            :  * This program is distributed in the hope that it will be useful,
      13                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15                 :            :  * GNU General Public License for more details.
      16                 :            :  *
      17                 :            :  * You should have received a copy of the GNU General Public License
      18                 :            :  * along with this program; if not, write to the Free Software
      19                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
      20                 :            :  */
      21                 :            : 
      22                 :            : #include <config.h>
      23                 :            : 
      24                 :            : #include "remote-database.h"
      25                 :            : 
      26                 :            : #include <signal.h>
      27                 :            : 
      28                 :            : #include "api/msetinternal.h"
      29                 :            : #include "api/smallvector.h"
      30                 :            : #include "backends/inmemory/inmemory_positionlist.h"
      31                 :            : #include "net_postlist.h"
      32                 :            : #include "remote-document.h"
      33                 :            : #include "omassert.h"
      34                 :            : #include "realtime.h"
      35                 :            : #include "net/length.h"
      36                 :            : #include "net/serialise.h"
      37                 :            : #include "net/serialise-error.h"
      38                 :            : #include "remote_alltermslist.h"
      39                 :            : #include "remote_metadatatermlist.h"
      40                 :            : #include "remote_termlist.h"
      41                 :            : #include "serialise-double.h"
      42                 :            : #include "str.h"
      43                 :            : #include "stringutils.h" // For STRINGIZE().
      44                 :            : #include "weight/weightinternal.h"
      45                 :            : 
      46                 :            : #include <cerrno>
      47                 :            : #include <memory>
      48                 :            : #include <string>
      49                 :            : #include <vector>
      50                 :            : 
      51                 :            : #include "xapian/constants.h"
      52                 :            : #include "xapian/error.h"
      53                 :            : #include "xapian/matchspy.h"
      54                 :            : 
      55                 :            : using namespace std;
      56                 :            : using Xapian::Internal::intrusive_ptr;
      57                 :            : 
      58                 :            : [[noreturn]]
      59                 :            : static void
      60                 :          0 : throw_handshake_failed(const string & context)
      61                 :            : {
      62                 :            :     throw Xapian::NetworkError("Handshake failed - is this a Xapian server?",
      63 [ #  # ][ #  # ]:          0 :                                context);
      64                 :            : }
      65                 :            : 
      66                 :            : [[noreturn]]
      67                 :            : static void
      68                 :          0 : throw_connection_closed_unexpectedly()
      69                 :            : {
      70 [ #  # ][ #  # ]:          0 :     throw Xapian::NetworkError("Connection closed unexpectedly");
                 [ #  # ]
      71                 :            : }
      72                 :            : 
      73                 :        718 : RemoteDatabase::RemoteDatabase(int fd, double timeout_,
      74                 :            :                                const string & context_, bool writable,
      75                 :            :                                int flags)
      76                 :            :     : Xapian::Database::Internal(writable ?
      77                 :            :                                  TRANSACTION_NONE :
      78                 :            :                                  TRANSACTION_READONLY),
      79                 :            :       link(fd, fd, context_),
      80                 :            :       context(context_),
      81                 :            :       cached_stats_valid(),
      82                 :            :       mru_valstats(),
      83                 :            :       mru_slot(Xapian::BAD_VALUENO),
      84 [ +  + ][ +  - ]:        718 :       timeout(timeout_)
         [ +  - ][ +  - ]
                 [ +  - ]
      85                 :            : {
      86                 :            : #ifndef __WIN32__
      87                 :            :     // It's simplest to just ignore SIGPIPE.  We'll still know if the
      88                 :            :     // connection dies because we'll get EPIPE back from write().
      89         [ -  + ]:        718 :     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
      90 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Couldn't set SIGPIPE to SIG_IGN", errno);
      91                 :            :     }
      92                 :            : #endif
      93                 :            : 
      94 [ +  - ][ +  - ]:        718 :     update_stats(MSG_MAX);
      95                 :            : 
      96         [ +  + ]:        718 :     if (writable) {
      97         [ -  + ]:        174 :         if (flags & Xapian::DB_RETRY_LOCK) {
      98         [ #  # ]:          0 :             const string & body = encode_length(flags & Xapian::DB_RETRY_LOCK);
      99         [ #  # ]:          0 :             update_stats(MSG_WRITEACCESS, body);
     100                 :            :         } else {
     101 [ +  - ][ +  - ]:        174 :             update_stats(MSG_WRITEACCESS);
     102                 :            :         }
     103                 :            :     }
     104                 :        718 : }
     105                 :            : 
     106                 :            : Xapian::termcount
     107                 :        126 : RemoteDatabase::positionlist_count(Xapian::docid did,
     108                 :            :                                    const std::string& term) const
     109                 :            : {
     110 [ +  - ][ +  + ]:        126 :     if (cached_stats_valid && !has_positional_info)
     111                 :         24 :         return 0;
     112                 :            : 
     113 [ +  - ][ +  - ]:        102 :     send_message(MSG_POSITIONLISTCOUNT, encode_length(did) + term);
                 [ +  - ]
     114                 :            : 
     115         [ +  - ]:        102 :     string message;
     116         [ +  - ]:        102 :     get_message(message, REPLY_POSITIONLISTCOUNT);
     117                 :        102 :     const char * p = message.data();
     118                 :        102 :     const char * p_end = p + message.size();
     119                 :            :     Xapian::termcount count;
     120         [ +  - ]:        102 :     decode_length(&p, p_end, count);
     121         [ -  + ]:        102 :     if (p != p_end) {
     122 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad REPLY_POSITIONLISTCOUNT message received", context);
     123                 :            :     }
     124                 :        126 :     return count;
     125                 :            : }
     126                 :            : 
     127                 :            : void
     128                 :         24 : RemoteDatabase::keep_alive()
     129                 :            : {
     130 [ +  - ][ +  + ]:         24 :     send_message(MSG_KEEPALIVE, string());
     131         [ +  - ]:         20 :     string message;
     132         [ +  - ]:         20 :     get_message(message, REPLY_DONE);
     133                 :         20 : }
     134                 :            : 
     135                 :            : TermList*
     136                 :         18 : RemoteDatabase::open_metadata_keylist(const std::string& prefix) const
     137                 :            : {
     138         [ +  + ]:         18 :     send_message(MSG_METADATAKEYLIST, prefix);
     139         [ +  - ]:         16 :     string message;
     140         [ +  - ]:         16 :     get_message(message, REPLY_METADATAKEYLIST);
     141 [ +  - ][ +  - ]:         16 :     return new RemoteMetadataTermList(prefix, std::move(message));
     142                 :            : }
     143                 :            : 
     144                 :            : TermList *
     145                 :       6882 : RemoteDatabase::open_term_list(Xapian::docid did) const
     146                 :            : {
     147                 :            :     Assert(did);
     148                 :            : 
     149                 :            :     // Ensure that total_length and doccount are up-to-date.
     150 [ +  + ][ +  - ]:       6882 :     if (!cached_stats_valid) update_stats();
                 [ +  - ]
     151                 :            : 
     152 [ +  - ][ +  + ]:       6882 :     send_message(MSG_TERMLIST, encode_length(did));
     153                 :            : 
     154         [ +  - ]:       6878 :     string message;
     155         [ +  + ]:       6878 :     get_message(message, REPLY_TERMLIST0);
     156                 :        860 :     const char * p = message.c_str();
     157                 :        860 :     const char * p_end = p + message.size();
     158                 :            :     Xapian::termcount doclen;
     159         [ +  - ]:        860 :     decode_length(&p, p_end, doclen);
     160                 :            :     Xapian::termcount num_entries;
     161         [ +  - ]:        860 :     decode_length(&p, p_end, num_entries);
     162         [ -  + ]:        860 :     if (p != p_end) {
     163                 :            :         throw Xapian::NetworkError("Bad REPLY_TERMLIST0 message received",
     164 [ #  # ][ #  # ]:          0 :                                    context);
     165                 :            :     }
     166         [ +  - ]:       1720 :     string reply;
     167         [ +  - ]:        860 :     get_message(message, REPLY_TERMLIST);
     168                 :            :     return new RemoteTermList(num_entries, doclen, doccount, this, did,
     169 [ +  - ][ +  - ]:       7738 :                               std::move(message));
     170                 :            : }
     171                 :            : 
     172                 :            : TermList *
     173                 :         92 : RemoteDatabase::open_term_list_direct(Xapian::docid did) const
     174                 :            : {
     175                 :         92 :     return RemoteDatabase::open_term_list(did);
     176                 :            : }
     177                 :            : 
     178                 :            : TermList*
     179                 :         94 : RemoteDatabase::open_allterms(const string& prefix) const
     180                 :            : {
     181         [ +  + ]:         94 :     send_message(MSG_ALLTERMS, prefix);
     182         [ +  - ]:         90 :     string message;
     183         [ +  - ]:         90 :     get_message(message, REPLY_ALLTERMS);
     184 [ +  - ][ +  - ]:         90 :     return new RemoteAllTermsList(prefix, std::move(message));
     185                 :            : }
     186                 :            : 
     187                 :            : PostList *
     188                 :        266 : RemoteDatabase::open_post_list(const string& term) const
     189                 :            : {
     190                 :        266 :     return RemoteDatabase::open_leaf_post_list(term, false);
     191                 :            : }
     192                 :            : 
     193                 :            : LeafPostList *
     194                 :        266 : RemoteDatabase::open_leaf_post_list(const string& term, bool) const
     195                 :            : {
     196 [ +  - ][ +  + ]:        266 :     return new NetworkPostList(intrusive_ptr<const RemoteDatabase>(this), term);
     197                 :            : }
     198                 :            : 
     199                 :            : Xapian::doccount
     200                 :        266 : RemoteDatabase::read_post_list(const string &term, NetworkPostList & pl) const
     201                 :            : {
     202         [ +  + ]:        266 :     send_message(MSG_POSTLIST, term);
     203                 :            : 
     204         [ +  - ]:        262 :     string message;
     205         [ +  - ]:        262 :     get_message(message, REPLY_POSTLISTSTART);
     206                 :            : 
     207                 :        262 :     const char * p = message.data();
     208                 :        262 :     const char * p_end = p + message.size();
     209                 :            :     Xapian::doccount termfreq;
     210         [ +  - ]:        262 :     decode_length(&p, p_end, termfreq);
     211                 :            : 
     212 [ +  - ][ +  + ]:       6834 :     while (get_message_or_done(message, REPLY_POSTLISTITEM)) {
     213         [ +  - ]:       6572 :         pl.append_posting(message);
     214                 :            :     }
     215                 :            : 
     216                 :        262 :     return termfreq;
     217                 :            : }
     218                 :            : 
     219                 :            : PositionList *
     220                 :        614 : RemoteDatabase::open_position_list(Xapian::docid did, const string &term) const
     221                 :            : {
     222 [ +  - ][ +  - ]:        614 :     send_message(MSG_POSITIONLIST, encode_length(did) + term);
                 [ +  + ]
     223                 :            : 
     224                 :        612 :     Xapian::VecCOW<Xapian::termpos> positions;
     225                 :            : 
     226         [ +  - ]:       1224 :     string message;
     227                 :        612 :     Xapian::termpos lastpos = static_cast<Xapian::termpos>(-1);
     228 [ +  - ][ +  + ]:      16838 :     while (get_message_or_done(message, REPLY_POSITIONLIST)) {
     229                 :      16226 :         const char * p = message.data();
     230                 :      16226 :         const char * p_end = p + message.size();
     231                 :            :         Xapian::termpos inc;
     232         [ +  - ]:      16226 :         decode_length(&p, p_end, inc);
     233                 :      16226 :         lastpos += inc + 1;
     234         [ +  - ]:      16226 :         positions.push_back(lastpos);
     235                 :            :     }
     236                 :            : 
     237         [ +  - ]:       1224 :     return new InMemoryPositionList(std::move(positions));
     238                 :            : }
     239                 :            : 
     240                 :            : bool
     241                 :         14 : RemoteDatabase::has_positions() const
     242                 :            : {
     243 [ +  + ][ +  - ]:         14 :     if (!cached_stats_valid) update_stats();
     244                 :         14 :     return has_positional_info;
     245                 :            : }
     246                 :            : 
     247                 :            : bool
     248                 :         50 : RemoteDatabase::reopen()
     249                 :            : {
     250                 :         50 :     mru_slot = Xapian::BAD_VALUENO;
     251         [ +  + ]:         50 :     return update_stats(MSG_REOPEN);
     252                 :            : }
     253                 :            : 
     254                 :            : void
     255                 :         22 : RemoteDatabase::close()
     256                 :            : {
     257                 :         22 :     do_close();
     258                 :         22 : }
     259                 :            : 
     260                 :            : // Currently lazy is used:
     261                 :            : //
     262                 :            : // * To implement API flag Xapian::DOC_ASSUME_VALID which can be specified when
     263                 :            : //   calling method Database::get_document()
     264                 :            : //
     265                 :            : // * To read values for backends without streamed values in SlowValueList
     266                 :            : //
     267                 :            : // * If you call get_data(), values_begin() or values_count() on a Document
     268                 :            : //   object passed to a KeyMaker, MatchDecider, MatchSpy during the match
     269                 :            : //
     270                 :            : // The first is relevant to the remote backend, but doesn't happen during
     271                 :            : // the match.
     272                 :            : //
     273                 :            : // SlowValueList is used with the remote backend, but not to read values
     274                 :            : // during the match.
     275                 :            : //
     276                 :            : // KeyMaker and MatchSpy happens on the server with the remote backend, so
     277                 :            : // they aren't relevant here.
     278                 :            : //
     279                 :            : // So the cases which are relevant to the remote backend don't matter during
     280                 :            : // the match, and so we can ignore the lazy flag here without affecting matcher
     281                 :            : // performance.
     282                 :            : Xapian::Document::Internal *
     283                 :     235860 : RemoteDatabase::open_document(Xapian::docid did, bool /*lazy*/) const
     284                 :            : {
     285                 :            :     Assert(did);
     286                 :            : 
     287 [ +  - ][ +  + ]:     235860 :     send_message(MSG_DOCUMENT, encode_length(did));
     288         [ +  - ]:     235854 :     string doc_data;
     289         [ +  - ]:     471708 :     map<Xapian::valueno, string> values;
     290         [ +  + ]:     235854 :     get_message(doc_data, REPLY_DOCDATA);
     291                 :            : 
     292         [ +  - ]:     459644 :     string message;
     293 [ +  - ][ +  + ]:    1928248 :     while (get_message_or_done(message, REPLY_VALUE)) {
     294                 :    1698426 :         const char * p = message.data();
     295                 :    1698426 :         const char * p_end = p + message.size();
     296                 :            :         Xapian::valueno slot;
     297         [ +  - ]:    1698426 :         decode_length(&p, p_end, slot);
     298 [ +  - ][ +  - ]:    1698426 :         values.insert(make_pair(slot, string(p, p_end)));
     299                 :            :     }
     300                 :            : 
     301 [ +  - ][ +  - ]:     465676 :     return new RemoteDocument(this, did, doc_data, std::move(values));
     302                 :            : }
     303                 :            : 
     304                 :            : bool
     305                 :       1170 : RemoteDatabase::update_stats(message_type msg_code, const string & body) const
     306                 :            : {
     307                 :            :     // MSG_MAX signals that we're handling the opening greeting, which isn't in
     308                 :            :     // response to an explicit message.
     309         [ +  + ]:       1170 :     if (msg_code != MSG_MAX)
     310         [ +  + ]:        452 :         send_message(msg_code, body);
     311                 :            : 
     312         [ +  - ]:       1166 :     string message;
     313 [ +  - ][ +  + ]:       1166 :     if (!get_message_or_done(message, REPLY_UPDATE)) {
     314                 :            :         // The database was already open at the latest revision.
     315                 :         36 :         return false;
     316                 :            :     }
     317                 :            : 
     318         [ -  + ]:       1130 :     if (message.size() < 3) {
     319                 :          0 :         throw_handshake_failed(context);
     320                 :            :     }
     321                 :       1130 :     const char *p = message.c_str();
     322                 :       1130 :     const char *p_end = p + message.size();
     323                 :            : 
     324                 :            :     // The protocol major versions must match.  The protocol minor version of
     325                 :            :     // the server must be >= that of the client.
     326                 :       1130 :     int protocol_major = static_cast<unsigned char>(*p++);
     327                 :       1130 :     int protocol_minor = static_cast<unsigned char>(*p++);
     328 [ +  - ][ -  + ]:       1130 :     if (protocol_major != XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION ||
     329                 :            :         protocol_minor < XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION) {
     330         [ #  # ]:          0 :         string errmsg("Server supports protocol version");
     331         [ #  # ]:          0 :         if (protocol_minor) {
     332         [ #  # ]:          0 :             errmsg += "s ";
     333 [ #  # ][ #  # ]:          0 :             errmsg += str(protocol_major);
     334         [ #  # ]:          0 :             errmsg += ".0 to ";
     335                 :            :         }
     336 [ #  # ][ #  # ]:          0 :         errmsg += str(protocol_major);
     337         [ #  # ]:          0 :         errmsg += '.';
     338 [ #  # ][ #  # ]:          0 :         errmsg += str(protocol_minor);
     339                 :            :         errmsg +=
     340                 :            :             " - client is using "
     341                 :            :             STRINGIZE(XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION)
     342                 :            :             "."
     343         [ #  # ]:          0 :             STRINGIZE(XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION);
     344         [ #  # ]:          0 :         throw Xapian::NetworkError(errmsg, context);
     345                 :            :     }
     346                 :            : 
     347         [ +  - ]:       1130 :     decode_length(&p, p_end, doccount);
     348         [ +  - ]:       1130 :     decode_length(&p, p_end, lastdocid);
     349                 :       1130 :     lastdocid += doccount;
     350         [ +  - ]:       1130 :     decode_length(&p, p_end, doclen_lbound);
     351         [ +  - ]:       1130 :     decode_length(&p, p_end, doclen_ubound);
     352                 :       1130 :     doclen_ubound += doclen_lbound;
     353         [ -  + ]:       1130 :     if (p == p_end) {
     354 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad stats update message received", context);
     355                 :            :     }
     356                 :       1130 :     has_positional_info = (*p++ == '1');
     357         [ +  - ]:       1130 :     decode_length(&p, p_end, total_length);
     358         [ +  - ]:       1130 :     uuid.assign(p, p_end);
     359                 :       1130 :     cached_stats_valid = true;
     360                 :       1166 :     return true;
     361                 :            : }
     362                 :            : 
     363                 :            : Xapian::doccount
     364                 :      11214 : RemoteDatabase::get_doccount() const
     365                 :            : {
     366 [ +  + ][ +  - ]:      11214 :     if (!cached_stats_valid) update_stats();
     367                 :      11214 :     return doccount;
     368                 :            : }
     369                 :            : 
     370                 :            : Xapian::docid
     371                 :        926 : RemoteDatabase::get_lastdocid() const
     372                 :            : {
     373 [ +  + ][ +  - ]:        926 :     if (!cached_stats_valid) update_stats();
     374                 :        926 :     return lastdocid;
     375                 :            : }
     376                 :            : 
     377                 :            : Xapian::totallength
     378                 :        140 : RemoteDatabase::get_total_length() const
     379                 :            : {
     380 [ -  + ][ #  # ]:        140 :     if (!cached_stats_valid) update_stats();
     381                 :        140 :     return total_length;
     382                 :            : }
     383                 :            : 
     384                 :            : bool
     385                 :        160 : RemoteDatabase::term_exists(const string & tname) const
     386                 :            : {
     387         [ +  + ]:        160 :     if (tname.empty()) {
     388         [ +  - ]:         20 :         return get_doccount() != 0;
     389                 :            :     }
     390         [ +  + ]:        140 :     send_message(MSG_TERMEXISTS, tname);
     391         [ +  - ]:        138 :     string message;
     392                 :            :     reply_type type = get_message(message,
     393                 :            :                                   REPLY_TERMEXISTS,
     394         [ +  - ]:        138 :                                   REPLY_TERMDOESNTEXIST);
     395                 :        158 :     return (type == REPLY_TERMEXISTS);
     396                 :            : }
     397                 :            : 
     398                 :            : void
     399                 :       3694 : RemoteDatabase::get_freqs(const string & term,
     400                 :            :                           Xapian::doccount * termfreq_ptr,
     401                 :            :                           Xapian::termcount * collfreq_ptr) const
     402                 :            : {
     403                 :            :     Assert(!term.empty());
     404         [ +  - ]:       3694 :     string message;
     405                 :            :     const char * p;
     406                 :            :     const char * p_end;
     407         [ +  + ]:       3694 :     if (termfreq_ptr) {
     408         [ -  + ]:        840 :         if (collfreq_ptr) {
     409         [ #  # ]:          0 :             send_message(MSG_FREQS, term);
     410         [ #  # ]:          0 :             get_message(message, REPLY_FREQS);
     411                 :            :         } else {
     412         [ +  + ]:        840 :             send_message(MSG_TERMFREQ, term);
     413         [ +  - ]:        838 :             get_message(message, REPLY_TERMFREQ);
     414                 :            :         }
     415                 :        838 :         p = message.data();
     416                 :        838 :         p_end = p + message.size();
     417         [ +  - ]:        838 :         decode_length(&p, p_end, *termfreq_ptr);
     418         [ +  - ]:       2854 :     } else if (collfreq_ptr) {
     419         [ +  + ]:       2854 :         send_message(MSG_COLLFREQ, term);
     420         [ +  - ]:       2852 :         get_message(message, REPLY_COLLFREQ);
     421                 :       2852 :         p = message.data();
     422                 :       2852 :         p_end = p + message.size();
     423                 :            :     }
     424         [ +  + ]:       3690 :     if (collfreq_ptr) {
     425         [ +  - ]:       2852 :         decode_length(&p, p_end, *collfreq_ptr);
     426                 :       3694 :     }
     427                 :       3690 : }
     428                 :            : 
     429                 :            : void
     430                 :        350 : RemoteDatabase::read_value_stats(Xapian::valueno slot) const
     431                 :            : {
     432         [ +  + ]:        350 :     if (mru_slot != slot) {
     433 [ +  - ][ +  + ]:        114 :         send_message(MSG_VALUESTATS, encode_length(slot));
     434         [ +  - ]:        108 :         string message;
     435         [ +  - ]:        108 :         get_message(message, REPLY_VALUESTATS);
     436                 :        108 :         const char * p = message.data();
     437                 :        108 :         const char * p_end = p + message.size();
     438                 :        108 :         mru_slot = slot;
     439         [ +  - ]:        108 :         decode_length(&p, p_end, mru_valstats.freq);
     440                 :            :         size_t len;
     441         [ +  - ]:        108 :         decode_length_and_check(&p, p_end, len);
     442         [ +  - ]:        108 :         mru_valstats.lower_bound.assign(p, len);
     443                 :        108 :         p += len;
     444         [ +  - ]:        108 :         decode_length_and_check(&p, p_end, len);
     445         [ +  - ]:        108 :         mru_valstats.upper_bound.assign(p, len);
     446                 :        108 :         p += len;
     447         [ -  + ]:        108 :         if (p != p_end) {
     448 [ #  # ][ #  # ]:          0 :             throw Xapian::NetworkError("Bad REPLY_VALUESTATS message received", context);
     449                 :        108 :         }
     450                 :            :     }
     451                 :        344 : }
     452                 :            : 
     453                 :            : Xapian::doccount
     454                 :        130 : RemoteDatabase::get_value_freq(Xapian::valueno slot) const
     455                 :            : {
     456                 :        130 :     read_value_stats(slot);
     457                 :        128 :     return mru_valstats.freq;
     458                 :            : }
     459                 :            : 
     460                 :            : std::string
     461                 :         98 : RemoteDatabase::get_value_lower_bound(Xapian::valueno slot) const
     462                 :            : {
     463                 :         98 :     read_value_stats(slot);
     464                 :         96 :     return mru_valstats.lower_bound;
     465                 :            : }
     466                 :            : 
     467                 :            : std::string
     468                 :        122 : RemoteDatabase::get_value_upper_bound(Xapian::valueno slot) const
     469                 :            : {
     470                 :        122 :     read_value_stats(slot);
     471                 :        120 :     return mru_valstats.upper_bound;
     472                 :            : }
     473                 :            : 
     474                 :            : Xapian::termcount
     475                 :         64 : RemoteDatabase::get_doclength_lower_bound() const
     476                 :            : {
     477                 :         64 :     return doclen_lbound;
     478                 :            : }
     479                 :            : 
     480                 :            : Xapian::termcount
     481                 :         48 : RemoteDatabase::get_doclength_upper_bound() const
     482                 :            : {
     483                 :         48 :     return doclen_ubound;
     484                 :            : }
     485                 :            : 
     486                 :            : Xapian::termcount
     487                 :        128 : RemoteDatabase::get_wdf_upper_bound(const string &) const
     488                 :            : {
     489                 :            :     // The default implementation returns get_collection_freq(), but we
     490                 :            :     // don't want the overhead of a remote message and reply per query
     491                 :            :     // term, and we can get called in the middle of a remote exchange
     492                 :            :     // too.  FIXME: handle this bound in the stats local/remote code...
     493                 :        128 :     return doclen_ubound;
     494                 :            : }
     495                 :            : 
     496                 :            : Xapian::termcount
     497                 :       6278 : RemoteDatabase::get_doclength(Xapian::docid did) const
     498                 :            : {
     499                 :            :     Assert(did != 0);
     500 [ +  - ][ +  + ]:       6278 :     send_message(MSG_DOCLENGTH, encode_length(did));
     501         [ +  - ]:       6268 :     string message;
     502         [ +  + ]:       6268 :     get_message(message, REPLY_DOCLENGTH);
     503                 :        254 :     const char * p = message.c_str();
     504                 :        254 :     const char * p_end = p + message.size();
     505                 :            :     Xapian::termcount doclen;
     506         [ +  - ]:        254 :     decode_length(&p, p_end, doclen);
     507         [ -  + ]:        254 :     if (p != p_end) {
     508 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad REPLY_DOCLENGTH message received", context);
     509                 :            :     }
     510                 :       6268 :     return doclen;
     511                 :            : }
     512                 :            : 
     513                 :            : Xapian::termcount
     514                 :       6166 : RemoteDatabase::get_unique_terms(Xapian::docid did) const
     515                 :            : {
     516                 :            :     Assert(did != 0);
     517 [ +  - ][ +  + ]:       6166 :     send_message(MSG_UNIQUETERMS, encode_length(did));
     518         [ +  - ]:       6156 :     string message;
     519         [ +  + ]:       6156 :     get_message(message, REPLY_UNIQUETERMS);
     520                 :        144 :     const char * p = message.c_str();
     521                 :        144 :     const char * p_end = p + message.size();
     522                 :            :     Xapian::termcount doclen;
     523         [ +  - ]:        144 :     decode_length(&p, p_end, doclen);
     524         [ -  + ]:        144 :     if (p != p_end) {
     525 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad REPLY_UNIQUETERMS message received", context);
     526                 :            :     }
     527                 :       6156 :     return doclen;
     528                 :            : }
     529                 :            : 
     530                 :            : reply_type
     531                 :    2288506 : RemoteDatabase::get_message(string &result,
     532                 :            :                             reply_type required_type,
     533                 :            :                             reply_type required_type2) const
     534                 :            : {
     535                 :    2288506 :     double end_time = RealTime::end_time(timeout);
     536                 :    2288506 :     int type = link.get_message(result, end_time);
     537         [ -  + ]:    2288506 :     if (type < 0)
     538                 :          0 :         throw_connection_closed_unexpectedly();
     539         [ -  + ]:    2288506 :     if (rare(type) >= REPLY_MAX) {
     540         [ #  # ]:          0 :         if (required_type == REPLY_UPDATE)
     541                 :          0 :             throw_handshake_failed(context);
     542         [ #  # ]:          0 :         string errmsg("Invalid reply type ");
     543 [ #  # ][ #  # ]:          0 :         errmsg += str(type);
     544 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError(errmsg);
     545                 :            :     }
     546         [ +  + ]:    2288506 :     if (type == REPLY_EXCEPTION) {
     547         [ +  - ]:      24196 :         unserialise_error(result, "REMOTE:", context);
     548                 :            :     }
     549 [ +  + ][ -  + ]:    2264310 :     if (type != required_type && type != required_type2) {
     550         [ #  # ]:          0 :         string errmsg("Expecting reply type ");
     551 [ #  # ][ #  # ]:          0 :         errmsg += str(int(required_type));
     552         [ #  # ]:          0 :         if (required_type2 != required_type) {
     553         [ #  # ]:          0 :             errmsg += " or ";
     554 [ #  # ][ #  # ]:          0 :             errmsg += str(int(required_type2));
     555                 :            :         }
     556         [ #  # ]:          0 :         errmsg += ", got ";
     557 [ #  # ][ #  # ]:          0 :         errmsg += str(type);
     558 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError(errmsg);
     559                 :            :     }
     560                 :            : 
     561                 :    2264310 :     return static_cast<reply_type>(type);
     562                 :            : }
     563                 :            : 
     564                 :            : void
     565                 :     335720 : RemoteDatabase::send_message(message_type type, const string &message) const
     566                 :            : {
     567                 :     335720 :     double end_time = RealTime::end_time(timeout);
     568                 :     335720 :     link.send_message(static_cast<unsigned char>(type), message, end_time);
     569                 :     335620 : }
     570                 :            : 
     571                 :            : void
     572                 :        740 : RemoteDatabase::do_close()
     573                 :            : {
     574                 :            :     // The dtor hasn't really been called!  FIXME: This works, but means any
     575                 :            :     // exceptions from end_transaction()/commit() are swallowed, which is
     576                 :            :     // not entirely desirable.
     577                 :        740 :     dtor_called();
     578                 :            : 
     579         [ +  + ]:        740 :     if (!is_read_only()) {
     580                 :            :         // If we're writable, send a shutdown message to the server and wait
     581                 :            :         // for it to close its end of the connection so we know that changes
     582                 :            :         // have been written and flushed, and the database write lock released.
     583                 :            :         // For the non-writable case, there's no need to wait - it would just
     584                 :            :         // slow down searching needlessly.
     585                 :        188 :         link.shutdown();
     586                 :            :     }
     587                 :        740 :     link.do_close();
     588                 :        740 : }
     589                 :            : 
     590                 :            : void
     591                 :      10420 : RemoteDatabase::set_query(const Xapian::Query& query,
     592                 :            :                           Xapian::termcount qlen,
     593                 :            :                           Xapian::valueno collapse_key,
     594                 :            :                           Xapian::doccount collapse_max,
     595                 :            :                           Xapian::Enquire::docid_order order,
     596                 :            :                           Xapian::valueno sort_key,
     597                 :            :                           Xapian::Enquire::Internal::sort_setting sort_by,
     598                 :            :                           bool sort_value_forward,
     599                 :            :                           double time_limit,
     600                 :            :                           int percent_threshold, double weight_threshold,
     601                 :            :                           const Xapian::Weight& wtscheme,
     602                 :            :                           const Xapian::RSet &omrset,
     603                 :            :                           const vector<opt_ptr_spy>& matchspies) const
     604                 :            : {
     605         [ +  + ]:      10420 :     string tmp = query.serialise();
     606         [ +  - ]:      20832 :     string message = encode_length(tmp.size());
     607         [ +  - ]:      10416 :     message += tmp;
     608                 :            : 
     609                 :            :     // Serialise assorted Enquire settings.
     610 [ +  - ][ +  - ]:      10416 :     message += encode_length(qlen);
     611 [ +  - ][ +  - ]:      10416 :     message += encode_length(collapse_max);
     612 [ +  + ][ +  - ]:      10416 :     if (collapse_max) message += encode_length(collapse_key);
                 [ +  - ]
     613         [ +  - ]:      10416 :     message += char('0' + order);
     614 [ +  - ][ +  - ]:      10416 :     message += encode_length(sort_key);
     615         [ +  - ]:      10416 :     message += char('0' + sort_by);
     616         [ +  - ]:      10416 :     message += char('0' + sort_value_forward);
     617 [ +  - ][ +  - ]:      10416 :     message += serialise_double(time_limit);
     618         [ +  - ]:      10416 :     message += char(percent_threshold);
     619 [ +  - ][ +  - ]:      10416 :     message += serialise_double(weight_threshold);
     620                 :            : 
     621 [ +  - ][ +  - ]:      10416 :     tmp = wtscheme.name();
     622 [ +  - ][ +  - ]:      10416 :     message += encode_length(tmp.size());
     623         [ +  - ]:      10416 :     message += tmp;
     624                 :            : 
     625 [ +  - ][ +  - ]:      10416 :     tmp = wtscheme.serialise();
     626 [ +  - ][ +  - ]:      10416 :     message += encode_length(tmp.size());
     627         [ +  - ]:      10416 :     message += tmp;
     628                 :            : 
     629 [ +  - ][ +  - ]:      10416 :     tmp = serialise_rset(omrset);
     630 [ +  - ][ +  - ]:      10416 :     message += encode_length(tmp.size());
     631         [ +  - ]:      10416 :     message += tmp;
     632                 :            : 
     633         [ +  + ]:      10420 :     for (auto i : matchspies) {
     634 [ +  - ][ +  - ]:          4 :         tmp = i->name();
     635         [ -  + ]:          4 :         if (tmp.empty()) {
     636 [ #  # ][ #  # ]:          0 :             throw Xapian::UnimplementedError("MatchSpy subclass not suitable for use with remote searches - name() method returned empty string");
                 [ #  # ]
     637                 :            :         }
     638 [ +  - ][ +  - ]:          4 :         message += encode_length(tmp.size());
     639         [ +  - ]:          4 :         message += tmp;
     640                 :            : 
     641 [ +  - ][ +  - ]:          4 :         tmp = i->serialise();
     642 [ +  - ][ +  - ]:          4 :         message += encode_length(tmp.size());
     643         [ +  - ]:          4 :         message += tmp;
     644                 :          4 :     }
     645                 :            : 
     646         [ +  + ]:      20832 :     send_message(MSG_QUERY, message);
     647                 :      10414 : }
     648                 :            : 
     649                 :            : void
     650                 :      10414 : RemoteDatabase::get_remote_stats(Xapian::Weight::Internal& out) const
     651                 :            : {
     652         [ +  - ]:      10414 :     string message;
     653         [ +  - ]:      10414 :     get_message(message, REPLY_STATS);
     654         [ +  - ]:      10414 :     unserialise_stats(message, out);
     655                 :      10414 : }
     656                 :            : 
     657                 :            : void
     658                 :      10414 : RemoteDatabase::send_global_stats(Xapian::doccount first,
     659                 :            :                                   Xapian::doccount maxitems,
     660                 :            :                                   Xapian::doccount check_at_least,
     661                 :            :                                   const Xapian::Weight::Internal &stats) const
     662                 :            : {
     663         [ +  - ]:      10414 :     string message = encode_length(first);
     664 [ +  - ][ +  - ]:      10414 :     message += encode_length(maxitems);
     665 [ +  - ][ +  - ]:      10414 :     message += encode_length(check_at_least);
     666 [ +  - ][ +  - ]:      10414 :     message += serialise_stats(stats);
     667         [ +  - ]:      10414 :     send_message(MSG_GETMSET, message);
     668                 :      10414 : }
     669                 :            : 
     670                 :            : Xapian::MSet
     671                 :      10414 : RemoteDatabase::get_mset(const vector<opt_ptr_spy>& matchspies) const
     672                 :            : {
     673         [ +  - ]:      10414 :     string message;
     674         [ +  + ]:      10414 :     get_message(message, REPLY_RESULTS);
     675                 :      10394 :     const char * p = message.data();
     676                 :      10394 :     const char * p_end = p + message.size();
     677                 :            : 
     678         [ +  + ]:      10398 :     for (auto i : matchspies) {
     679         [ -  + ]:          4 :         if (p == p_end)
     680 [ #  # ][ #  # ]:          0 :             throw Xapian::NetworkError("Expected serialised matchspy");
                 [ #  # ]
     681                 :            :         size_t len;
     682         [ +  - ]:          4 :         decode_length_and_check(&p, p_end, len);
     683         [ +  - ]:          8 :         string spyresults(p, len);
     684                 :          4 :         p += len;
     685         [ +  - ]:          4 :         i->merge_results(spyresults);
     686                 :          4 :     }
     687         [ +  - ]:      10394 :     Xapian::MSet mset;
     688         [ +  - ]:      10394 :     mset.internal->unserialise(p, p_end);
     689                 :      10414 :     return mset;
     690                 :            : }
     691                 :            : 
     692                 :            : void
     693                 :       4386 : RemoteDatabase::commit()
     694                 :            : {
     695 [ +  - ][ +  + ]:       4386 :     send_message(MSG_COMMIT, string());
     696                 :            : 
     697                 :            :     // We need to wait for a response to ensure documents have been committed.
     698         [ +  - ]:       4366 :     string message;
     699         [ +  + ]:       4366 :     get_message(message, REPLY_DONE);
     700                 :       4362 : }
     701                 :            : 
     702                 :            : void
     703                 :         14 : RemoteDatabase::cancel()
     704                 :            : {
     705                 :         14 :     cached_stats_valid = false;
     706                 :         14 :     mru_slot = Xapian::BAD_VALUENO;
     707                 :            : 
     708 [ +  - ][ +  - ]:         14 :     send_message(MSG_CANCEL, string());
     709         [ +  - ]:         14 :     string dummy;
     710         [ +  - ]:         14 :     get_message(dummy, REPLY_DONE);
     711                 :         14 : }
     712                 :            : 
     713                 :            : Xapian::docid
     714                 :      37436 : RemoteDatabase::add_document(const Xapian::Document & doc)
     715                 :            : {
     716                 :      37436 :     cached_stats_valid = false;
     717                 :      37436 :     mru_slot = Xapian::BAD_VALUENO;
     718                 :            : 
     719 [ +  - ][ +  + ]:      37436 :     send_message(MSG_ADDDOCUMENT, serialise_document(doc));
     720                 :            : 
     721         [ +  - ]:      37434 :     string message;
     722         [ +  + ]:      37434 :     get_message(message, REPLY_ADDDOCUMENT);
     723                 :            : 
     724                 :      37342 :     const char * p = message.data();
     725                 :      37342 :     const char * p_end = p + message.size();
     726                 :            :     Xapian::docid did;
     727         [ +  - ]:      37342 :     decode_length(&p, p_end, did);
     728                 :      37434 :     return did;
     729                 :            : }
     730                 :            : 
     731                 :            : void
     732                 :       6054 : RemoteDatabase::delete_document(Xapian::docid did)
     733                 :            : {
     734                 :       6054 :     cached_stats_valid = false;
     735                 :       6054 :     mru_slot = Xapian::BAD_VALUENO;
     736                 :            : 
     737 [ +  - ][ +  + ]:       6054 :     send_message(MSG_DELETEDOCUMENT, encode_length(did));
     738         [ +  - ]:       6052 :     string dummy;
     739         [ +  + ]:       6052 :     get_message(dummy, REPLY_DONE);
     740                 :       6050 : }
     741                 :            : 
     742                 :            : void
     743                 :          6 : RemoteDatabase::delete_document(const std::string & unique_term)
     744                 :            : {
     745                 :          6 :     cached_stats_valid = false;
     746                 :          6 :     mru_slot = Xapian::BAD_VALUENO;
     747                 :            : 
     748         [ +  - ]:          6 :     send_message(MSG_DELETEDOCUMENTTERM, unique_term);
     749         [ +  - ]:          6 :     string dummy;
     750         [ +  - ]:          6 :     get_message(dummy, REPLY_DONE);
     751                 :          6 : }
     752                 :            : 
     753                 :            : void
     754                 :       6130 : RemoteDatabase::replace_document(Xapian::docid did,
     755                 :            :                                  const Xapian::Document & doc)
     756                 :            : {
     757                 :       6130 :     cached_stats_valid = false;
     758                 :       6130 :     mru_slot = Xapian::BAD_VALUENO;
     759                 :            : 
     760         [ +  - ]:       6130 :     string message = encode_length(did);
     761 [ +  - ][ +  - ]:       6130 :     message += serialise_document(doc);
     762                 :            : 
     763         [ +  + ]:       6130 :     send_message(MSG_REPLACEDOCUMENT, message);
     764         [ +  - ]:      12252 :     string dummy;
     765         [ +  + ]:      12256 :     get_message(dummy, REPLY_DONE);
     766                 :       6124 : }
     767                 :            : 
     768                 :            : Xapian::docid
     769                 :         40 : RemoteDatabase::replace_document(const std::string & unique_term,
     770                 :            :                                  const Xapian::Document & doc)
     771                 :            : {
     772                 :         40 :     cached_stats_valid = false;
     773                 :         40 :     mru_slot = Xapian::BAD_VALUENO;
     774                 :            : 
     775         [ +  - ]:         40 :     string message = encode_length(unique_term.size());
     776         [ +  - ]:         40 :     message += unique_term;
     777 [ +  - ][ +  - ]:         40 :     message += serialise_document(doc);
     778                 :            : 
     779         [ +  + ]:         40 :     send_message(MSG_REPLACEDOCUMENTTERM, message);
     780                 :            : 
     781         [ +  - ]:         38 :     get_message(message, REPLY_ADDDOCUMENT);
     782                 :            : 
     783                 :         38 :     const char * p = message.data();
     784                 :         38 :     const char * p_end = p + message.size();
     785                 :            :     Xapian::docid did;
     786         [ +  - ]:         38 :     decode_length(&p, p_end, did);
     787                 :         40 :     return did;
     788                 :            : }
     789                 :            : 
     790                 :            : string
     791                 :         16 : RemoteDatabase::get_uuid() const
     792                 :            : {
     793                 :         16 :     return uuid;
     794                 :            : }
     795                 :            : 
     796                 :            : string
     797                 :         50 : RemoteDatabase::get_metadata(const string & key) const
     798                 :            : {
     799                 :         50 :     send_message(MSG_GETMETADATA, key);
     800                 :         46 :     string metadata;
     801         [ +  - ]:         46 :     get_message(metadata, REPLY_METADATA);
     802                 :         46 :     return metadata;
     803                 :            : }
     804                 :            : 
     805                 :            : void
     806                 :         44 : RemoteDatabase::set_metadata(const string & key, const string & value)
     807                 :            : {
     808         [ +  - ]:         44 :     string data = encode_length(key.size());
     809         [ +  - ]:         44 :     data += key;
     810         [ +  - ]:         44 :     data += value;
     811         [ +  + ]:         44 :     send_message(MSG_SETMETADATA, data);
     812         [ +  - ]:         84 :     string dummy;
     813         [ +  - ]:         86 :     get_message(dummy, REPLY_DONE);
     814                 :         42 : }
     815                 :            : 
     816                 :            : void
     817                 :         10 : RemoteDatabase::add_spelling(const string & word,
     818                 :            :                              Xapian::termcount freqinc) const
     819                 :            : {
     820         [ +  - ]:         10 :     string data = encode_length(freqinc);
     821         [ +  - ]:         10 :     data += word;
     822         [ +  - ]:         10 :     send_message(MSG_ADDSPELLING, data);
     823         [ +  - ]:         20 :     string dummy;
     824         [ +  - ]:         20 :     get_message(dummy, REPLY_DONE);
     825                 :         10 : }
     826                 :            : 
     827                 :            : Xapian::termcount
     828                 :         16 : RemoteDatabase::remove_spelling(const string & word,
     829                 :            :                                 Xapian::termcount freqdec) const
     830                 :            : {
     831         [ +  - ]:         16 :     string data = encode_length(freqdec);
     832         [ +  - ]:         16 :     data += word;
     833         [ +  - ]:         16 :     send_message(MSG_REMOVESPELLING, data);
     834                 :            : 
     835         [ +  - ]:         32 :     string message;
     836         [ +  - ]:         16 :     get_message(message, REPLY_REMOVESPELLING);
     837                 :         16 :     const char * p = message.data();
     838                 :         16 :     const char * p_end = p + message.size();
     839                 :            :     Xapian::termcount result;
     840         [ +  - ]:         16 :     decode_length(&p, p_end, result);
     841         [ -  + ]:         16 :     if (p != p_end) {
     842 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad REPLY_REMOVESPELLING message received", context);
     843                 :            :     }
     844                 :         16 :     return result;
     845                 :            : }
     846                 :            : 
     847                 :            : bool
     848                 :          0 : RemoteDatabase::locked() const
     849                 :            : {
     850 [ #  # ][ #  # ]:          0 :     throw Xapian::UnimplementedError("Database::locked() not implemented for remote backend");
                 [ #  # ]
     851                 :            : }
     852                 :            : 
     853                 :            : string
     854                 :          2 : RemoteDatabase::get_description() const
     855                 :            : {
     856         [ +  - ]:          2 :     string desc = "Remote(context=";
     857         [ +  - ]:          2 :     desc += context;
     858         [ +  - ]:          2 :     desc += ')';
     859                 :          2 :     return desc;
     860                 :            : }

Generated by: LCOV version 1.11