LCOV - code coverage report
Current view: top level - backends/remote - remote-database.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 954b5873a738 Lines: 365 423 86.3 %
Date: 2019-06-30 05:20:33 Functions: 49 52 94.2 %
Branches: 301 638 47.2 %

           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/serialise.h"
      36                 :            : #include "net/serialise-error.h"
      37                 :            : #include "pack.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                 :        774 : 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 [ +  + ][ +  - ]:        774 :       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         [ -  + ]:        774 :     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 [ +  - ][ +  - ]:        774 :     update_stats(MSG_MAX);
      95                 :            : 
      96         [ +  + ]:        774 :     if (writable) {
      97         [ -  + ]:        174 :         if (flags & Xapian::DB_RETRY_LOCK) {
      98         [ #  # ]:          0 :             string message;
      99         [ #  # ]:          0 :             pack_uint_last(message, unsigned(flags & Xapian::DB_RETRY_LOCK));
     100         [ #  # ]:          0 :             update_stats(MSG_WRITEACCESS, message);
     101                 :            :         } else {
     102 [ +  - ][ +  - ]:        174 :             update_stats(MSG_WRITEACCESS);
     103                 :            :         }
     104                 :            :     }
     105                 :        774 : }
     106                 :            : 
     107                 :            : Xapian::termcount
     108                 :        126 : RemoteDatabase::positionlist_count(Xapian::docid did,
     109                 :            :                                    const std::string& term) const
     110                 :            : {
     111 [ +  - ][ +  + ]:        126 :     if (cached_stats_valid && !has_positional_info)
     112                 :         24 :         return 0;
     113                 :            : 
     114         [ +  - ]:        102 :     string message;
     115         [ +  - ]:        102 :     pack_uint(message, did);
     116         [ +  - ]:        102 :     message += term;
     117         [ +  - ]:        102 :     send_message(MSG_POSITIONLISTCOUNT, message);
     118                 :            : 
     119         [ +  - ]:        102 :     get_message(message, REPLY_POSITIONLISTCOUNT);
     120                 :        102 :     const char * p = message.data();
     121                 :        102 :     const char * p_end = p + message.size();
     122                 :            :     Xapian::termcount count;
     123         [ -  + ]:        102 :     if (!unpack_uint_last(&p, p_end, &count)) {
     124 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad REPLY_POSITIONLISTCOUNT", context);
     125                 :            :     }
     126                 :        126 :     return count;
     127                 :            : }
     128                 :            : 
     129                 :            : void
     130                 :         24 : RemoteDatabase::keep_alive()
     131                 :            : {
     132 [ +  - ][ +  + ]:         24 :     send_message(MSG_KEEPALIVE, string());
     133         [ +  - ]:         20 :     string message;
     134         [ +  - ]:         20 :     get_message(message, REPLY_DONE);
     135                 :         20 : }
     136                 :            : 
     137                 :            : TermList*
     138                 :         18 : RemoteDatabase::open_metadata_keylist(const std::string& prefix) const
     139                 :            : {
     140         [ +  + ]:         18 :     send_message(MSG_METADATAKEYLIST, prefix);
     141         [ +  - ]:         16 :     string message;
     142         [ +  - ]:         16 :     get_message(message, REPLY_METADATAKEYLIST);
     143 [ +  - ][ +  - ]:         16 :     return new RemoteMetadataTermList(prefix, std::move(message));
     144                 :            : }
     145                 :            : 
     146                 :            : TermList *
     147                 :       6998 : RemoteDatabase::open_term_list(Xapian::docid did) const
     148                 :            : {
     149                 :            :     Assert(did);
     150                 :            : 
     151                 :            :     // Ensure that total_length and doccount are up-to-date.
     152 [ +  + ][ +  - ]:       6998 :     if (!cached_stats_valid) update_stats();
                 [ +  - ]
     153                 :            : 
     154         [ +  - ]:       6998 :     string message;
     155         [ +  - ]:       6998 :     pack_uint_last(message, did);
     156         [ +  + ]:       6998 :     send_message(MSG_TERMLIST, message);
     157                 :            : 
     158         [ +  + ]:       6994 :     get_message(message, REPLY_TERMLISTHEADER);
     159                 :        976 :     const char * p = message.c_str();
     160                 :        976 :     const char * p_end = p + message.size();
     161                 :            :     Xapian::termcount doclen;
     162                 :            :     Xapian::termcount num_entries;
     163   [ +  -  -  + ]:       1952 :     if (!unpack_uint(&p, p_end, &doclen) ||
                 [ -  + ]
     164                 :        976 :         !unpack_uint_last(&p, p_end, &num_entries)) {
     165 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad REPLY_TERMLISTHEADER", context);
     166                 :            :     }
     167         [ +  - ]:        976 :     get_message(message, REPLY_TERMLIST);
     168                 :            :     return new RemoteTermList(num_entries, doclen, doccount, this, did,
     169 [ +  - ][ +  - ]:       6998 :                               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 :     send_message(MSG_POSTLIST, term);
     197                 :            : 
     198         [ +  - ]:        262 :     string message;
     199         [ +  - ]:        262 :     get_message(message, REPLY_POSTLISTHEADER);
     200                 :            : 
     201                 :        262 :     const char * p = message.data();
     202                 :        262 :     const char * p_end = p + message.size();
     203                 :            :     Xapian::doccount termfreq;
     204         [ -  + ]:        262 :     if (!unpack_uint_last(&p, p_end, &termfreq)) {
     205                 :          0 :         unpack_throw_serialisation_error(p);
     206                 :            :     }
     207                 :            : 
     208         [ +  - ]:        262 :     get_message(message, REPLY_POSTLIST);
     209                 :            : 
     210                 :            :     return new NetworkPostList(intrusive_ptr<const RemoteDatabase>(this),
     211                 :            :                                term,
     212                 :            :                                termfreq,
     213 [ +  - ][ +  - ]:        262 :                                std::move(message));
     214                 :            : }
     215                 :            : 
     216                 :            : PositionList *
     217                 :        616 : RemoteDatabase::open_position_list(Xapian::docid did, const string &term) const
     218                 :            : {
     219         [ +  - ]:        616 :     string message;
     220         [ +  - ]:        616 :     pack_uint(message, did);
     221         [ +  - ]:        616 :     message += term;
     222         [ +  + ]:        616 :     send_message(MSG_POSITIONLIST, message);
     223                 :            : 
     224                 :       1228 :     Xapian::VecCOW<Xapian::termpos> positions;
     225                 :            : 
     226         [ +  - ]:        614 :     get_message(message, REPLY_POSITIONLIST);
     227                 :        614 :     Xapian::termpos lastpos = static_cast<Xapian::termpos>(-1);
     228                 :        614 :     const char* p = message.data();
     229                 :        614 :     const char* p_end = p + message.size();
     230         [ +  + ]:      16840 :     while (p != p_end) {
     231                 :            :         Xapian::termpos inc;
     232         [ -  + ]:      16226 :         if (!unpack_uint(&p, p_end, &inc)) {
     233                 :          0 :             unpack_throw_serialisation_error(p);
     234                 :            :         }
     235                 :      16226 :         lastpos += inc + 1;
     236         [ +  - ]:      16226 :         positions.push_back(lastpos);
     237                 :            :     }
     238                 :            : 
     239         [ +  - ]:       1230 :     return new InMemoryPositionList(std::move(positions));
     240                 :            : }
     241                 :            : 
     242                 :            : bool
     243                 :      10656 : RemoteDatabase::has_positions() const
     244                 :            : {
     245 [ +  + ][ +  - ]:      10656 :     if (!cached_stats_valid) update_stats();
     246                 :      10656 :     return has_positional_info;
     247                 :            : }
     248                 :            : 
     249                 :            : bool
     250                 :         50 : RemoteDatabase::reopen()
     251                 :            : {
     252                 :         50 :     mru_slot = Xapian::BAD_VALUENO;
     253         [ +  + ]:         50 :     return update_stats(MSG_REOPEN);
     254                 :            : }
     255                 :            : 
     256                 :            : void
     257                 :         22 : RemoteDatabase::close()
     258                 :            : {
     259                 :         22 :     do_close();
     260                 :         22 : }
     261                 :            : 
     262                 :            : // Currently lazy is used:
     263                 :            : //
     264                 :            : // * To implement API flag Xapian::DOC_ASSUME_VALID which can be specified when
     265                 :            : //   calling method Database::get_document()
     266                 :            : //
     267                 :            : // * To read values for backends without streamed values in SlowValueList
     268                 :            : //
     269                 :            : // * If you call get_data(), values_begin() or values_count() on a Document
     270                 :            : //   object passed to a KeyMaker, MatchDecider, MatchSpy during the match
     271                 :            : //
     272                 :            : // The first is relevant to the remote backend, but doesn't happen during
     273                 :            : // the match.
     274                 :            : //
     275                 :            : // SlowValueList is used with the remote backend, but not to read values
     276                 :            : // during the match.
     277                 :            : //
     278                 :            : // KeyMaker and MatchSpy happens on the server with the remote backend, so
     279                 :            : // they aren't relevant here.
     280                 :            : //
     281                 :            : // So the cases which are relevant to the remote backend don't matter during
     282                 :            : // the match, and so we can ignore the lazy flag here without affecting matcher
     283                 :            : // performance.
     284                 :            : Xapian::Document::Internal *
     285                 :     235998 : RemoteDatabase::open_document(Xapian::docid did, bool /*lazy*/) const
     286                 :            : {
     287                 :            :     Assert(did);
     288                 :            : 
     289         [ +  - ]:     235998 :     string message;
     290         [ +  - ]:     235998 :     pack_uint_last(message, did);
     291         [ +  + ]:     235998 :     send_message(MSG_DOCUMENT, message);
     292                 :            : 
     293         [ +  - ]:     471984 :     string doc_data;
     294         [ +  + ]:     235992 :     get_message(doc_data, REPLY_DOCDATA);
     295                 :            : 
     296         [ +  - ]:     459920 :     map<Xapian::valueno, string> values;
     297 [ +  - ][ +  + ]:    1929562 :     while (get_message_or_done(message, REPLY_VALUE)) {
     298                 :    1699602 :         const char * p = message.data();
     299                 :    1699602 :         const char * p_end = p + message.size();
     300                 :            :         Xapian::valueno slot;
     301         [ -  + ]:    1699602 :         if (!unpack_uint(&p, p_end, &slot)) {
     302                 :          0 :             unpack_throw_serialisation_error(p);
     303                 :            :         }
     304 [ +  - ][ +  - ]:    1699602 :         values.insert(make_pair(slot, string(p, p_end)));
                 [ +  - ]
     305                 :            :     }
     306                 :            : 
     307                 :            :     return new RemoteDocument(this, did, std::move(doc_data),
     308 [ +  - ][ +  - ]:     465958 :                               std::move(values));
     309                 :            : }
     310                 :            : 
     311                 :            : bool
     312                 :       1226 : RemoteDatabase::update_stats(message_type msg_code, const string & body) const
     313                 :            : {
     314                 :            :     // MSG_MAX signals that we're handling the opening greeting, which isn't in
     315                 :            :     // response to an explicit message.
     316         [ +  + ]:       1226 :     if (msg_code != MSG_MAX)
     317         [ +  + ]:        452 :         send_message(msg_code, body);
     318                 :            : 
     319         [ +  - ]:       1222 :     string message;
     320 [ +  - ][ +  + ]:       1222 :     if (!get_message_or_done(message, REPLY_UPDATE)) {
     321                 :            :         // The database was already open at the latest revision.
     322                 :         36 :         return false;
     323                 :            :     }
     324                 :            : 
     325         [ -  + ]:       1186 :     if (message.size() < 3) {
     326                 :          0 :         throw_handshake_failed(context);
     327                 :            :     }
     328                 :       1186 :     const char *p = message.c_str();
     329                 :       1186 :     const char *p_end = p + message.size();
     330                 :            : 
     331                 :            :     // The protocol major versions must match.  The protocol minor version of
     332                 :            :     // the server must be >= that of the client.
     333                 :       1186 :     int protocol_major = static_cast<unsigned char>(*p++);
     334                 :       1186 :     int protocol_minor = static_cast<unsigned char>(*p++);
     335 [ +  - ][ -  + ]:       1186 :     if (protocol_major != XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION ||
     336                 :            :         protocol_minor < XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION) {
     337         [ #  # ]:          0 :         string errmsg("Server supports protocol version");
     338         [ #  # ]:          0 :         if (protocol_minor) {
     339         [ #  # ]:          0 :             errmsg += "s ";
     340 [ #  # ][ #  # ]:          0 :             errmsg += str(protocol_major);
     341         [ #  # ]:          0 :             errmsg += ".0 to ";
     342                 :            :         }
     343 [ #  # ][ #  # ]:          0 :         errmsg += str(protocol_major);
     344         [ #  # ]:          0 :         errmsg += '.';
     345 [ #  # ][ #  # ]:          0 :         errmsg += str(protocol_minor);
     346                 :            :         errmsg +=
     347                 :            :             " - client is using "
     348                 :            :             STRINGIZE(XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION)
     349                 :            :             "."
     350         [ #  # ]:          0 :             STRINGIZE(XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION);
     351         [ #  # ]:          0 :         throw Xapian::NetworkError(errmsg, context);
     352                 :            :     }
     353                 :            : 
     354 [ +  - ][ -  + ]:       3558 :     if (!unpack_uint(&p, p_end, &doccount) ||
     355         [ +  - ]:       2372 :         !unpack_uint(&p, p_end, &lastdocid) ||
     356         [ +  - ]:       2372 :         !unpack_uint(&p, p_end, &doclen_lbound) ||
     357         [ +  - ]:       2372 :         !unpack_uint(&p, p_end, &doclen_ubound) ||
     358 [ +  - ][ -  + ]:       3558 :         !unpack_bool(&p, p_end, &has_positional_info) ||
     359                 :       1186 :         !unpack_uint(&p, p_end, &total_length)) {
     360 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad stats update message received", context);
     361                 :            :     }
     362                 :       1186 :     lastdocid += doccount;
     363                 :       1186 :     doclen_ubound += doclen_lbound;
     364         [ +  - ]:       1186 :     uuid.assign(p, p_end);
     365                 :       1186 :     cached_stats_valid = true;
     366                 :       1222 :     return true;
     367                 :            : }
     368                 :            : 
     369                 :            : Xapian::doccount
     370                 :      11474 : RemoteDatabase::get_doccount() const
     371                 :            : {
     372 [ +  + ][ +  - ]:      11474 :     if (!cached_stats_valid) update_stats();
     373                 :      11474 :     return doccount;
     374                 :            : }
     375                 :            : 
     376                 :            : Xapian::docid
     377                 :        926 : RemoteDatabase::get_lastdocid() const
     378                 :            : {
     379 [ +  + ][ +  - ]:        926 :     if (!cached_stats_valid) update_stats();
     380                 :        926 :     return lastdocid;
     381                 :            : }
     382                 :            : 
     383                 :            : Xapian::totallength
     384                 :        140 : RemoteDatabase::get_total_length() const
     385                 :            : {
     386 [ -  + ][ #  # ]:        140 :     if (!cached_stats_valid) update_stats();
     387                 :        140 :     return total_length;
     388                 :            : }
     389                 :            : 
     390                 :            : bool
     391                 :        164 : RemoteDatabase::term_exists(const string & tname) const
     392                 :            : {
     393         [ +  + ]:        164 :     if (tname.empty()) {
     394         [ +  - ]:         20 :         return get_doccount() != 0;
     395                 :            :     }
     396         [ +  + ]:        144 :     send_message(MSG_TERMEXISTS, tname);
     397         [ +  - ]:        142 :     string message;
     398                 :            :     reply_type type = get_message(message,
     399                 :            :                                   REPLY_TERMEXISTS,
     400         [ +  - ]:        142 :                                   REPLY_TERMDOESNTEXIST);
     401                 :        162 :     return (type == REPLY_TERMEXISTS);
     402                 :            : }
     403                 :            : 
     404                 :            : void
     405                 :       3872 : RemoteDatabase::get_freqs(const string & term,
     406                 :            :                           Xapian::doccount * termfreq_ptr,
     407                 :            :                           Xapian::termcount * collfreq_ptr) const
     408                 :            : {
     409                 :            :     Assert(!term.empty());
     410         [ +  - ]:       3872 :     string message;
     411 [ +  + ][ -  + ]:       3872 :     if (termfreq_ptr && collfreq_ptr) {
     412         [ #  # ]:          0 :         send_message(MSG_FREQS, term);
     413         [ #  # ]:          0 :         get_message(message, REPLY_FREQS);
     414                 :          0 :         const char* p = message.data();
     415                 :          0 :         const char* p_end = p + message.size();
     416   [ #  #  #  # ]:          0 :         if (unpack_uint(&p, p_end, termfreq_ptr) &&
                 [ #  # ]
     417                 :          0 :             unpack_uint_last(&p, p_end, collfreq_ptr)) {
     418                 :          0 :             return;
     419                 :          0 :         }
     420         [ +  + ]:       3872 :     } else if (termfreq_ptr) {
     421         [ +  + ]:       1018 :         send_message(MSG_TERMFREQ, term);
     422         [ +  - ]:       1016 :         get_message(message, REPLY_TERMFREQ);
     423                 :       1016 :         const char* p = message.data();
     424                 :       1016 :         const char* p_end = p + message.size();
     425         [ +  - ]:       1016 :         if (unpack_uint_last(&p, p_end, termfreq_ptr)) {
     426                 :       1016 :             return;
     427                 :            :         }
     428         [ +  - ]:       2854 :     } else if (collfreq_ptr) {
     429         [ +  + ]:       2854 :         send_message(MSG_COLLFREQ, term);
     430         [ +  - ]:       2852 :         get_message(message, REPLY_COLLFREQ);
     431                 :       2852 :         const char* p = message.data();
     432                 :       2852 :         const char* p_end = p + message.size();
     433         [ +  - ]:       2852 :         if (unpack_uint_last(&p, p_end, collfreq_ptr)) {
     434                 :       2852 :             return;
     435                 :            :         }
     436                 :            :     } else {
     437                 :            :         Assert(false);
     438                 :          0 :         return;
     439                 :            :     }
     440                 :            :     throw Xapian::NetworkError("Bad REPLY_FREQS/REPLY_TERMFREQ/REPLY_COLLFREQ",
     441 [ #  # ][ #  # ]:       3872 :                                context);
     442                 :            : }
     443                 :            : 
     444                 :            : void
     445                 :        352 : RemoteDatabase::read_value_stats(Xapian::valueno slot) const
     446                 :            : {
     447         [ +  + ]:        352 :     if (mru_slot == slot)
     448                 :        346 :         return;
     449                 :            : 
     450         [ +  - ]:        116 :     string message;
     451         [ +  - ]:        116 :     pack_uint_last(message, slot);
     452         [ +  + ]:        116 :     send_message(MSG_VALUESTATS, message);
     453                 :            : 
     454         [ +  - ]:        110 :     get_message(message, REPLY_VALUESTATS);
     455                 :        110 :     const char* p = message.data();
     456                 :        110 :     const char* p_end = p + message.size();
     457                 :        110 :     mru_slot = slot;
     458 [ +  - ][ -  + ]:        220 :     if (!unpack_uint(&p, p_end, &mru_valstats.freq) ||
                 [ -  + ]
     459         [ +  - ]:        110 :         !unpack_string(&p, p_end, mru_valstats.lower_bound)) {
     460 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad REPLY_VALUESTATS", context);
     461                 :            :     }
     462         [ +  - ]:        116 :     mru_valstats.upper_bound.assign(p, p_end);
     463                 :            : }
     464                 :            : 
     465                 :            : Xapian::doccount
     466                 :        130 : RemoteDatabase::get_value_freq(Xapian::valueno slot) const
     467                 :            : {
     468                 :        130 :     read_value_stats(slot);
     469                 :        128 :     return mru_valstats.freq;
     470                 :            : }
     471                 :            : 
     472                 :            : std::string
     473                 :        100 : RemoteDatabase::get_value_lower_bound(Xapian::valueno slot) const
     474                 :            : {
     475                 :        100 :     read_value_stats(slot);
     476                 :         98 :     return mru_valstats.lower_bound;
     477                 :            : }
     478                 :            : 
     479                 :            : std::string
     480                 :        122 : RemoteDatabase::get_value_upper_bound(Xapian::valueno slot) const
     481                 :            : {
     482                 :        122 :     read_value_stats(slot);
     483                 :        120 :     return mru_valstats.upper_bound;
     484                 :            : }
     485                 :            : 
     486                 :            : Xapian::termcount
     487                 :         64 : RemoteDatabase::get_doclength_lower_bound() const
     488                 :            : {
     489                 :         64 :     return doclen_lbound;
     490                 :            : }
     491                 :            : 
     492                 :            : Xapian::termcount
     493                 :         48 : RemoteDatabase::get_doclength_upper_bound() const
     494                 :            : {
     495                 :         48 :     return doclen_ubound;
     496                 :            : }
     497                 :            : 
     498                 :            : Xapian::termcount
     499                 :        128 : RemoteDatabase::get_wdf_upper_bound(const string &) const
     500                 :            : {
     501                 :            :     // The default implementation returns get_collection_freq(), but we
     502                 :            :     // don't want the overhead of a remote message and reply per query
     503                 :            :     // term, and we can get called in the middle of a remote exchange
     504                 :            :     // too.  FIXME: handle this bound in the stats local/remote code...
     505                 :        128 :     return doclen_ubound;
     506                 :            : }
     507                 :            : 
     508                 :            : Xapian::termcount
     509                 :       6282 : RemoteDatabase::get_doclength(Xapian::docid did) const
     510                 :            : {
     511                 :            :     Assert(did != 0);
     512         [ +  - ]:       6282 :     string message;
     513         [ +  - ]:       6282 :     pack_uint_last(message, did);
     514         [ +  + ]:       6282 :     send_message(MSG_DOCLENGTH, message);
     515                 :            : 
     516         [ +  + ]:       6272 :     get_message(message, REPLY_DOCLENGTH);
     517                 :        258 :     const char* p = message.c_str();
     518                 :        258 :     const char* p_end = p + message.size();
     519                 :            :     Xapian::termcount doclen;
     520         [ -  + ]:        258 :     if (!unpack_uint_last(&p, p_end, &doclen)) {
     521 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad REPLY_DOCLENGTH", context);
     522                 :            :     }
     523                 :       6282 :     return doclen;
     524                 :            : }
     525                 :            : 
     526                 :            : Xapian::termcount
     527                 :       6170 : RemoteDatabase::get_unique_terms(Xapian::docid did) const
     528                 :            : {
     529                 :            :     Assert(did != 0);
     530         [ +  - ]:       6170 :     string message;
     531         [ +  - ]:       6170 :     pack_uint_last(message, did);
     532         [ +  + ]:       6170 :     send_message(MSG_UNIQUETERMS, message);
     533                 :            : 
     534         [ +  + ]:       6160 :     get_message(message, REPLY_UNIQUETERMS);
     535                 :        148 :     const char* p = message.c_str();
     536                 :        148 :     const char* p_end = p + message.size();
     537                 :            :     Xapian::termcount doclen;
     538         [ -  + ]:        148 :     if (!unpack_uint_last(&p, p_end, &doclen)) {
     539 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad REPLY_DOCLENGTH", context);
     540                 :            :     }
     541                 :       6170 :     return doclen;
     542                 :            : }
     543                 :            : 
     544                 :            : reply_type
     545                 :    2268132 : RemoteDatabase::get_message(string &result,
     546                 :            :                             reply_type required_type,
     547                 :            :                             reply_type required_type2) const
     548                 :            : {
     549                 :    2268132 :     double end_time = RealTime::end_time(timeout);
     550                 :    2268132 :     int type = link.get_message(result, end_time);
     551         [ -  + ]:    2268132 :     if (type < 0)
     552                 :          0 :         throw_connection_closed_unexpectedly();
     553         [ -  + ]:    2268132 :     if (rare(type) >= REPLY_MAX) {
     554         [ #  # ]:          0 :         if (required_type == REPLY_UPDATE)
     555                 :          0 :             throw_handshake_failed(context);
     556         [ #  # ]:          0 :         string errmsg("Invalid reply type ");
     557 [ #  # ][ #  # ]:          0 :         errmsg += str(type);
     558 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError(errmsg);
     559                 :            :     }
     560         [ +  + ]:    2268132 :     if (type == REPLY_EXCEPTION) {
     561         [ +  - ]:      24196 :         unserialise_error(result, "REMOTE:", context);
     562                 :            :     }
     563 [ +  + ][ -  + ]:    2243936 :     if (type != required_type && type != required_type2) {
     564         [ #  # ]:          0 :         string errmsg("Expecting reply type ");
     565 [ #  # ][ #  # ]:          0 :         errmsg += str(int(required_type));
     566         [ #  # ]:          0 :         if (required_type2 != required_type) {
     567         [ #  # ]:          0 :             errmsg += " or ";
     568 [ #  # ][ #  # ]:          0 :             errmsg += str(int(required_type2));
     569                 :            :         }
     570         [ #  # ]:          0 :         errmsg += ", got ";
     571 [ #  # ][ #  # ]:          0 :         errmsg += str(type);
     572 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError(errmsg);
     573                 :            :     }
     574                 :            : 
     575                 :    2243936 :     return static_cast<reply_type>(type);
     576                 :            : }
     577                 :            : 
     578                 :            : void
     579                 :     336658 : RemoteDatabase::send_message(message_type type, const string &message) const
     580                 :            : {
     581                 :     336658 :     double end_time = RealTime::end_time(timeout);
     582                 :     336658 :     link.send_message(static_cast<unsigned char>(type), message, end_time);
     583                 :     336558 : }
     584                 :            : 
     585                 :            : void
     586                 :        796 : RemoteDatabase::do_close()
     587                 :            : {
     588                 :            :     // The dtor hasn't really been called!  FIXME: This works, but means any
     589                 :            :     // exceptions from end_transaction()/commit() are swallowed, which is
     590                 :            :     // not entirely desirable.
     591                 :        796 :     dtor_called();
     592                 :            : 
     593         [ +  + ]:        796 :     if (!is_read_only()) {
     594                 :            :         // If we're writable, send a shutdown message to the server and wait
     595                 :            :         // for it to close its end of the connection so we know that changes
     596                 :            :         // have been written and flushed, and the database write lock released.
     597                 :            :         // For the non-writable case, there's no need to wait - it would just
     598                 :            :         // slow down searching needlessly.
     599                 :        188 :         link.shutdown();
     600                 :            :     }
     601                 :        796 :     link.do_close();
     602                 :        796 : }
     603                 :            : 
     604                 :            : void
     605                 :      10664 : RemoteDatabase::set_query(const Xapian::Query& query,
     606                 :            :                           Xapian::termcount qlen,
     607                 :            :                           Xapian::valueno collapse_key,
     608                 :            :                           Xapian::doccount collapse_max,
     609                 :            :                           Xapian::Enquire::docid_order order,
     610                 :            :                           Xapian::valueno sort_key,
     611                 :            :                           Xapian::Enquire::Internal::sort_setting sort_by,
     612                 :            :                           bool sort_value_forward,
     613                 :            :                           double time_limit,
     614                 :            :                           int percent_threshold, double weight_threshold,
     615                 :            :                           const Xapian::Weight& wtscheme,
     616                 :            :                           const Xapian::RSet &omrset,
     617                 :            :                           const vector<opt_ptr_spy>& matchspies,
     618                 :            :                           bool full_db_has_positions) const
     619                 :            : {
     620         [ +  - ]:      10664 :     string message;
     621 [ +  + ][ +  - ]:      10664 :     pack_string(message, query.serialise());
     622                 :            : 
     623                 :            :     // Serialise assorted Enquire settings.
     624         [ +  - ]:      10660 :     pack_uint(message, qlen);
     625         [ +  - ]:      10660 :     pack_uint(message, collapse_max);
     626 [ +  + ][ +  - ]:      10660 :     if (collapse_max) pack_uint(message, collapse_key);
     627         [ +  - ]:      10660 :     message += char(order);
     628         [ +  - ]:      10660 :     message += char(sort_by);
     629         [ +  + ]:      10660 :     if (sort_by != Xapian::Enquire::Internal::REL) {
     630         [ +  - ]:        248 :         pack_uint(message, sort_key);
     631                 :            :     }
     632         [ +  - ]:      10660 :     pack_bool(message, sort_value_forward);
     633         [ +  - ]:      10660 :     pack_bool(message, full_db_has_positions);
     634 [ +  - ][ +  - ]:      10660 :     message += serialise_double(time_limit);
     635         [ +  - ]:      10660 :     message += char(percent_threshold);
     636 [ +  - ][ +  - ]:      10660 :     message += serialise_double(weight_threshold);
     637                 :            : 
     638 [ +  - ][ +  - ]:      10660 :     pack_string(message, wtscheme.name());
     639                 :            : 
     640 [ +  - ][ +  - ]:      10660 :     pack_string(message, wtscheme.serialise());
     641                 :            : 
     642 [ +  - ][ +  - ]:      10660 :     pack_string(message, serialise_rset(omrset));
     643                 :            : 
     644         [ +  + ]:      10682 :     for (auto i : matchspies) {
     645         [ +  - ]:         44 :         const string& name = i->name();
     646         [ -  + ]:         22 :         if (name.empty()) {
     647 [ #  # ][ #  # ]:          0 :             throw Xapian::UnimplementedError("MatchSpy subclass not suitable for use with remote searches - name() method returned empty string");
                 [ #  # ]
     648                 :            :         }
     649         [ +  - ]:         22 :         pack_string(message, name);
     650 [ +  - ][ +  - ]:         22 :         pack_string(message, i->serialise());
     651                 :         22 :     }
     652                 :            : 
     653         [ +  + ]:      10664 :     send_message(MSG_QUERY, message);
     654                 :      10658 : }
     655                 :            : 
     656                 :            : void
     657                 :      10658 : RemoteDatabase::get_remote_stats(Xapian::Weight::Internal& out) const
     658                 :            : {
     659         [ +  - ]:      10658 :     string message;
     660         [ +  - ]:      10658 :     get_message(message, REPLY_STATS);
     661         [ +  - ]:      10658 :     unserialise_stats(message, out);
     662                 :      10658 : }
     663                 :            : 
     664                 :            : void
     665                 :      10658 : RemoteDatabase::send_global_stats(Xapian::doccount first,
     666                 :            :                                   Xapian::doccount maxitems,
     667                 :            :                                   Xapian::doccount check_at_least,
     668                 :            :                                   const Xapian::Weight::Internal &stats) const
     669                 :            : {
     670         [ +  - ]:      10658 :     string message;
     671         [ +  - ]:      10658 :     pack_uint(message, first);
     672         [ +  - ]:      10658 :     pack_uint(message, maxitems);
     673         [ +  - ]:      10658 :     pack_uint(message, check_at_least);
     674 [ +  - ][ +  - ]:      10658 :     message += serialise_stats(stats);
     675         [ +  - ]:      10658 :     send_message(MSG_GETMSET, message);
     676                 :      10658 : }
     677                 :            : 
     678                 :            : Xapian::MSet
     679                 :      10658 : RemoteDatabase::get_mset(const vector<opt_ptr_spy>& matchspies) const
     680                 :            : {
     681         [ +  - ]:      10658 :     string message;
     682         [ +  + ]:      10658 :     get_message(message, REPLY_RESULTS);
     683                 :      10638 :     const char * p = message.data();
     684                 :      10638 :     const char * p_end = p + message.size();
     685                 :            : 
     686         [ +  - ]:      21276 :     string spyresults;
     687         [ +  + ]:      10660 :     for (auto i : matchspies) {
     688 [ +  - ][ -  + ]:         22 :         if (!unpack_string(&p, p_end, spyresults)) {
     689 [ #  # ][ #  # ]:          0 :             throw Xapian::NetworkError("Expected serialised matchspy");
                 [ #  # ]
     690                 :            :         }
     691         [ +  - ]:         22 :         i->merge_results(spyresults);
     692                 :         22 :     }
     693         [ +  - ]:      10638 :     Xapian::MSet mset;
     694         [ +  - ]:      10638 :     mset.internal->unserialise(p, p_end);
     695                 :      21296 :     return mset;
     696                 :            : }
     697                 :            : 
     698                 :            : void
     699                 :       4386 : RemoteDatabase::commit()
     700                 :            : {
     701 [ +  - ][ +  + ]:       4386 :     send_message(MSG_COMMIT, string());
     702                 :            : 
     703                 :            :     // We need to wait for a response to ensure documents have been committed.
     704         [ +  - ]:       4366 :     string message;
     705         [ +  + ]:       4366 :     get_message(message, REPLY_DONE);
     706                 :       4362 : }
     707                 :            : 
     708                 :            : void
     709                 :         14 : RemoteDatabase::cancel()
     710                 :            : {
     711                 :         14 :     cached_stats_valid = false;
     712                 :         14 :     mru_slot = Xapian::BAD_VALUENO;
     713                 :            : 
     714 [ +  - ][ +  - ]:         14 :     send_message(MSG_CANCEL, string());
     715         [ +  - ]:         14 :     string dummy;
     716         [ +  - ]:         14 :     get_message(dummy, REPLY_DONE);
     717                 :         14 : }
     718                 :            : 
     719                 :            : Xapian::docid
     720                 :      37436 : RemoteDatabase::add_document(const Xapian::Document & doc)
     721                 :            : {
     722                 :      37436 :     cached_stats_valid = false;
     723                 :      37436 :     mru_slot = Xapian::BAD_VALUENO;
     724                 :            : 
     725 [ +  - ][ +  + ]:      37436 :     send_message(MSG_ADDDOCUMENT, serialise_document(doc));
     726                 :            : 
     727         [ +  - ]:      37434 :     string message;
     728         [ +  + ]:      37434 :     get_message(message, REPLY_ADDDOCUMENT);
     729                 :            : 
     730                 :      37342 :     const char* p = message.data();
     731                 :      37342 :     const char* p_end = p + message.size();
     732                 :            :     Xapian::docid did;
     733         [ -  + ]:      37342 :     if (!unpack_uint_last(&p, p_end, &did)) {
     734                 :          0 :         unpack_throw_serialisation_error(p);
     735                 :            :     }
     736                 :      37434 :     return did;
     737                 :            : }
     738                 :            : 
     739                 :            : void
     740                 :       6054 : RemoteDatabase::delete_document(Xapian::docid did)
     741                 :            : {
     742                 :       6054 :     cached_stats_valid = false;
     743                 :       6054 :     mru_slot = Xapian::BAD_VALUENO;
     744                 :            : 
     745         [ +  - ]:       6054 :     string message;
     746         [ +  - ]:       6054 :     pack_uint_last(message, did);
     747         [ +  + ]:       6054 :     send_message(MSG_DELETEDOCUMENT, message);
     748                 :            : 
     749         [ +  + ]:       6054 :     get_message(message, REPLY_DONE);
     750                 :       6050 : }
     751                 :            : 
     752                 :            : void
     753                 :          6 : RemoteDatabase::delete_document(const std::string & unique_term)
     754                 :            : {
     755                 :          6 :     cached_stats_valid = false;
     756                 :          6 :     mru_slot = Xapian::BAD_VALUENO;
     757                 :            : 
     758         [ +  - ]:          6 :     send_message(MSG_DELETEDOCUMENTTERM, unique_term);
     759         [ +  - ]:          6 :     string dummy;
     760         [ +  - ]:          6 :     get_message(dummy, REPLY_DONE);
     761                 :          6 : }
     762                 :            : 
     763                 :            : void
     764                 :       6132 : RemoteDatabase::replace_document(Xapian::docid did,
     765                 :            :                                  const Xapian::Document & doc)
     766                 :            : {
     767                 :       6132 :     cached_stats_valid = false;
     768                 :       6132 :     mru_slot = Xapian::BAD_VALUENO;
     769                 :            : 
     770         [ +  - ]:       6132 :     string message;
     771         [ +  - ]:       6132 :     pack_uint(message, did);
     772 [ +  - ][ +  - ]:       6132 :     message += serialise_document(doc);
     773                 :            : 
     774         [ +  + ]:       6132 :     send_message(MSG_REPLACEDOCUMENT, message);
     775                 :            : 
     776         [ +  + ]:       6132 :     get_message(message, REPLY_DONE);
     777                 :       6126 : }
     778                 :            : 
     779                 :            : Xapian::docid
     780                 :         40 : RemoteDatabase::replace_document(const std::string & unique_term,
     781                 :            :                                  const Xapian::Document & doc)
     782                 :            : {
     783                 :         40 :     cached_stats_valid = false;
     784                 :         40 :     mru_slot = Xapian::BAD_VALUENO;
     785                 :            : 
     786         [ +  - ]:         40 :     string message;
     787         [ +  - ]:         40 :     pack_string(message, unique_term);
     788 [ +  - ][ +  - ]:         40 :     message += serialise_document(doc);
     789                 :            : 
     790         [ +  + ]:         40 :     send_message(MSG_REPLACEDOCUMENTTERM, message);
     791                 :            : 
     792         [ +  - ]:         38 :     get_message(message, REPLY_ADDDOCUMENT);
     793                 :            : 
     794                 :         38 :     const char* p = message.data();
     795                 :         38 :     const char* p_end = p + message.size();
     796                 :            :     Xapian::docid did;
     797         [ -  + ]:         38 :     if (!unpack_uint_last(&p, p_end, &did)) {
     798                 :          0 :         unpack_throw_serialisation_error(p);
     799                 :            :     }
     800                 :         40 :     return did;
     801                 :            : }
     802                 :            : 
     803                 :            : string
     804                 :         16 : RemoteDatabase::get_uuid() const
     805                 :            : {
     806                 :         16 :     return uuid;
     807                 :            : }
     808                 :            : 
     809                 :            : string
     810                 :         50 : RemoteDatabase::get_metadata(const string & key) const
     811                 :            : {
     812                 :         50 :     send_message(MSG_GETMETADATA, key);
     813                 :         46 :     string metadata;
     814         [ +  - ]:         46 :     get_message(metadata, REPLY_METADATA);
     815                 :         46 :     return metadata;
     816                 :            : }
     817                 :            : 
     818                 :            : void
     819                 :         44 : RemoteDatabase::set_metadata(const string & key, const string & value)
     820                 :            : {
     821         [ +  - ]:         44 :     string message;
     822         [ +  - ]:         44 :     pack_string(message, key);
     823         [ +  - ]:         44 :     message += value;
     824         [ +  + ]:         44 :     send_message(MSG_SETMETADATA, message);
     825                 :            : 
     826         [ +  - ]:         44 :     get_message(message, REPLY_DONE);
     827                 :         42 : }
     828                 :            : 
     829                 :            : void
     830                 :         10 : RemoteDatabase::add_spelling(const string & word,
     831                 :            :                              Xapian::termcount freqinc) const
     832                 :            : {
     833         [ +  - ]:         10 :     string message;
     834         [ +  - ]:         10 :     pack_uint(message, freqinc);
     835         [ +  - ]:         10 :     message += word;
     836         [ +  - ]:         10 :     send_message(MSG_ADDSPELLING, message);
     837                 :            : 
     838         [ +  - ]:         10 :     get_message(message, REPLY_DONE);
     839                 :         10 : }
     840                 :            : 
     841                 :            : Xapian::termcount
     842                 :         16 : RemoteDatabase::remove_spelling(const string & word,
     843                 :            :                                 Xapian::termcount freqdec) const
     844                 :            : {
     845         [ +  - ]:         16 :     string message;
     846         [ +  - ]:         16 :     pack_uint(message, freqdec);
     847         [ +  - ]:         16 :     message += word;
     848         [ +  - ]:         16 :     send_message(MSG_REMOVESPELLING, message);
     849                 :            : 
     850         [ +  - ]:         16 :     get_message(message, REPLY_REMOVESPELLING);
     851                 :         16 :     const char * p = message.data();
     852                 :         16 :     const char * p_end = p + message.size();
     853                 :            :     Xapian::termcount result;
     854         [ -  + ]:         16 :     if (!unpack_uint_last(&p, p_end, &result)) {
     855 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad REPLY_REMOVESPELLING", context);
     856                 :            :     }
     857                 :         16 :     return result;
     858                 :            : }
     859                 :            : 
     860                 :            : bool
     861                 :          0 : RemoteDatabase::locked() const
     862                 :            : {
     863 [ #  # ][ #  # ]:          0 :     throw Xapian::UnimplementedError("Database::locked() not implemented for remote backend");
                 [ #  # ]
     864                 :            : }
     865                 :            : 
     866                 :            : string
     867                 :          2 : RemoteDatabase::get_description() const
     868                 :            : {
     869         [ +  - ]:          2 :     string desc = "Remote(context=";
     870         [ +  - ]:          2 :     desc += context;
     871         [ +  - ]:          2 :     desc += ')';
     872                 :          2 :     return desc;
     873                 :            : }

Generated by: LCOV version 1.11