LCOV - code coverage report
Current view: top level - net - remoteserver.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 7028d852e609 Lines: 431 491 87.8 %
Date: 2019-02-17 14:59:59 Functions: 34 36 94.4 %
Branches: 393 838 46.9 %

           Branch data     Line data    Source code
       1                 :            : /** @file remoteserver.cc
       2                 :            :  *  @brief Xapian remote backend server base class
       3                 :            :  */
       4                 :            : /* Copyright (C) 2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 Olly Betts
       5                 :            :  * Copyright (C) 2006,2007,2009,2010 Lemur Consulting Ltd
       6                 :            :  *
       7                 :            :  * This program is free software; you can redistribute it and/or modify
       8                 :            :  * it under the terms of the GNU General Public License as published by
       9                 :            :  * the Free Software Foundation; either version 2 of the License, or
      10                 :            :  * (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                 :            : #include "remoteserver.h"
      24                 :            : 
      25                 :            : #include "xapian/constants.h"
      26                 :            : #include "xapian/database.h"
      27                 :            : #include "xapian/enquire.h"
      28                 :            : #include "xapian/error.h"
      29                 :            : #include "xapian/matchspy.h"
      30                 :            : #include "xapian/query.h"
      31                 :            : #include "xapian/rset.h"
      32                 :            : #include "xapian/valueiterator.h"
      33                 :            : 
      34                 :            : #include <signal.h>
      35                 :            : #include <cerrno>
      36                 :            : #include <cstdlib>
      37                 :            : #include <memory>
      38                 :            : 
      39                 :            : #include "api/msetinternal.h"
      40                 :            : #include "api/termlist.h"
      41                 :            : #include "length.h"
      42                 :            : #include "matcher/matcher.h"
      43                 :            : #include "omassert.h"
      44                 :            : #include "realtime.h"
      45                 :            : #include "serialise.h"
      46                 :            : #include "serialise-double.h"
      47                 :            : #include "serialise-error.h"
      48                 :            : #include "str.h"
      49                 :            : #include "stringutils.h"
      50                 :            : #include "weight/weightinternal.h"
      51                 :            : 
      52                 :            : using namespace std;
      53                 :            : 
      54                 :            : [[noreturn]]
      55                 :            : static void
      56                 :          0 : throw_read_only()
      57                 :            : {
      58 [ #  # ][ #  # ]:          0 :     throw Xapian::InvalidOperationError("Server is read-only");
                 [ #  # ]
      59                 :            : }
      60                 :            : 
      61                 :            : /// Class to throw when we receive the connection closing message.
      62                 :            : struct ConnectionClosed { };
      63                 :            : 
      64                 :        708 : RemoteServer::RemoteServer(const vector<string>& dbpaths,
      65                 :            :                            int fdin_, int fdout_,
      66                 :            :                            double active_timeout_, double idle_timeout_,
      67                 :            :                            bool writable_)
      68                 :            :     : RemoteConnection(fdin_, fdout_, string()),
      69                 :            :       db(NULL), wdb(NULL), writable(writable_),
      70 [ +  - ][ +  - ]:        708 :       active_timeout(active_timeout_), idle_timeout(idle_timeout_)
      71                 :            : {
      72                 :            :     // Catch errors opening the database and propagate them to the client.
      73                 :            :     try {
      74                 :            :         Assert(!dbpaths.empty());
      75                 :            :         // We always open the database read-only to start with.  If we're
      76                 :            :         // writable, the client can ask to be upgraded to write access once
      77                 :            :         // connected if it wants it.
      78 [ +  - ][ +  - ]:        708 :         db = new Xapian::Database(dbpaths[0]);
      79                 :            :         // Build a better description than Database::get_description() gives
      80                 :            :         // in the variable context.  FIXME: improve Database::get_description()
      81                 :            :         // and then just use that instead.
      82         [ +  - ]:        708 :         context = dbpaths[0];
      83                 :            : 
      84         [ +  + ]:        708 :         if (!writable) {
      85                 :        538 :             vector<string>::const_iterator i(dbpaths.begin());
      86         [ -  + ]:        538 :             for (++i; i != dbpaths.end(); ++i) {
      87 [ #  # ][ #  # ]:          0 :                 db->add_database(Xapian::Database(*i));
      88         [ #  # ]:          0 :                 context += ' ';
      89         [ #  # ]:          0 :                 context += *i;
      90                 :            :             }
      91                 :            :         } else {
      92                 :            :             AssertEq(dbpaths.size(), 1); // Expecting exactly one database.
      93                 :            :         }
      94         [ #  # ]:          0 :     } catch (const Xapian::Error &err) {
      95                 :            :         // Propagate the exception to the client.
      96   [ #  #  #  # ]:          0 :         send_message(REPLY_EXCEPTION, serialise_error(err));
      97                 :            :         // And rethrow it so our caller can log it and close the connection.
      98                 :          0 :         throw;
      99                 :            :     }
     100                 :            : 
     101                 :            : #ifndef __WIN32__
     102                 :            :     // It's simplest to just ignore SIGPIPE.  We'll still know if the
     103                 :            :     // connection dies because we'll get EPIPE back from write().
     104         [ -  + ]:        708 :     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
     105 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Couldn't set SIGPIPE to SIG_IGN", errno);
     106                 :            : #endif
     107                 :            : 
     108                 :            :     // Send greeting message.
     109 [ +  - ][ +  - ]:        708 :     msg_update(string());
     110                 :        708 : }
     111                 :            : 
     112                 :       1416 : RemoteServer::~RemoteServer()
     113                 :            : {
     114         [ +  - ]:        708 :     delete db;
     115                 :            :     // wdb is either NULL or equal to db, so we shouldn't delete it too!
     116                 :        708 : }
     117                 :            : 
     118                 :            : message_type
     119                 :     336208 : RemoteServer::get_message(double timeout, string & result,
     120                 :            :                           message_type required_type)
     121                 :            : {
     122                 :     336208 :     double end_time = RealTime::end_time(timeout);
     123                 :     336208 :     int type = RemoteConnection::get_message(result, end_time);
     124                 :            : 
     125                 :            :     // Handle "shutdown connection" message here.  Treat EOF here for a read-only
     126                 :            :     // database the same way since a read-only client just closes the
     127                 :            :     // connection when done.
     128 [ +  + ][ +  + ]:     336206 :     if (type == MSG_SHUTDOWN || (type < 0 && wdb == NULL))
                 [ +  - ]
     129                 :        706 :         throw ConnectionClosed();
     130         [ -  + ]:     335500 :     if (type < 0)
     131 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Connection closed unexpectedly");
                 [ #  # ]
     132         [ -  + ]:     335500 :     if (type >= MSG_MAX) {
     133         [ #  # ]:          0 :         string errmsg("Invalid message type ");
     134 [ #  # ][ #  # ]:          0 :         errmsg += str(type);
     135 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError(errmsg);
     136                 :            :     }
     137 [ +  + ][ -  + ]:     335500 :     if (required_type != MSG_MAX && type != int(required_type)) {
     138         [ #  # ]:          0 :         string errmsg("Expecting message type ");
     139 [ #  # ][ #  # ]:          0 :         errmsg += str(int(required_type));
     140         [ #  # ]:          0 :         errmsg += ", got ";
     141 [ #  # ][ #  # ]:          0 :         errmsg += str(type);
     142 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError(errmsg);
     143                 :            :     }
     144                 :     335500 :     return static_cast<message_type>(type);
     145                 :            : }
     146                 :            : 
     147                 :            : void
     148                 :    2282180 : RemoteServer::send_message(reply_type type, const string &message)
     149                 :            : {
     150                 :    2282180 :     double end_time = RealTime::end_time(active_timeout);
     151                 :    2282180 :     unsigned char type_as_char = static_cast<unsigned char>(type);
     152                 :    2282180 :     RemoteConnection::send_message(type_as_char, message, end_time);
     153                 :    2282180 : }
     154                 :            : 
     155                 :            : typedef void (RemoteServer::* dispatch_func)(const string &);
     156                 :            : 
     157                 :            : void
     158                 :        708 : RemoteServer::run()
     159                 :            : {
     160                 :            :     while (true) {
     161                 :            :         try {
     162         [ +  - ]:     325846 :             string message;
     163         [ +  + ]:     325846 :             size_t type = get_message(idle_timeout, message);
     164   [ +  +  +  +  :     325138 :             switch (type) {
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  -  +  
                   +  - ]
     165                 :            :                 case MSG_ALLTERMS:
     166         [ +  - ]:         90 :                     msg_allterms(message);
     167                 :         90 :                     continue;
     168                 :            :                 case MSG_COLLFREQ:
     169         [ +  - ]:       2852 :                     msg_collfreq(message);
     170                 :       2852 :                     continue;
     171                 :            :                 case MSG_DOCUMENT:
     172         [ +  + ]:     235854 :                     msg_document(message);
     173                 :     229822 :                     continue;
     174                 :            :                 case MSG_TERMEXISTS:
     175         [ +  - ]:        138 :                     msg_termexists(message);
     176                 :        138 :                     continue;
     177                 :            :                 case MSG_TERMFREQ:
     178         [ +  - ]:        838 :                     msg_termfreq(message);
     179                 :        838 :                     continue;
     180                 :            :                 case MSG_VALUESTATS:
     181         [ +  - ]:        108 :                     msg_valuestats(message);
     182                 :        108 :                     continue;
     183                 :            :                 case MSG_KEEPALIVE:
     184         [ +  - ]:         20 :                     msg_keepalive(message);
     185                 :         20 :                     continue;
     186                 :            :                 case MSG_DOCLENGTH:
     187         [ +  + ]:       6268 :                     msg_doclength(message);
     188                 :        254 :                     continue;
     189                 :            :                 case MSG_QUERY:
     190         [ +  + ]:      10362 :                     msg_query(message);
     191                 :      10350 :                     continue;
     192                 :            :                 case MSG_TERMLIST:
     193         [ +  + ]:       6878 :                     msg_termlist(message);
     194                 :        860 :                     continue;
     195                 :            :                 case MSG_POSITIONLIST:
     196         [ +  - ]:        612 :                     msg_positionlist(message);
     197                 :        612 :                     continue;
     198                 :            :                 case MSG_POSTLIST:
     199         [ +  - ]:        262 :                     msg_postlist(message);
     200                 :        262 :                     continue;
     201                 :            :                 case MSG_REOPEN:
     202         [ +  - ]:         46 :                     msg_reopen(message);
     203                 :         46 :                     continue;
     204                 :            :                 case MSG_UPDATE:
     205         [ +  - ]:        226 :                     msg_update(message);
     206                 :        226 :                     continue;
     207                 :            :                 case MSG_ADDDOCUMENT:
     208         [ +  + ]:      37432 :                     msg_adddocument(message);
     209                 :      37340 :                     continue;
     210                 :            :                 case MSG_CANCEL:
     211         [ +  - ]:         14 :                     msg_cancel(message);
     212                 :         14 :                     continue;
     213                 :            :                 case MSG_DELETEDOCUMENTTERM:
     214         [ +  - ]:          6 :                     msg_deletedocumentterm(message);
     215                 :          6 :                     continue;
     216                 :            :                 case MSG_COMMIT:
     217         [ +  + ]:       4360 :                     msg_commit(message);
     218                 :       4356 :                     continue;
     219                 :            :                 case MSG_REPLACEDOCUMENT:
     220         [ +  - ]:       6124 :                     msg_replacedocument(message);
     221                 :       6124 :                     continue;
     222                 :            :                 case MSG_REPLACEDOCUMENTTERM:
     223         [ +  - ]:         38 :                     msg_replacedocumentterm(message);
     224                 :         38 :                     continue;
     225                 :            :                 case MSG_DELETEDOCUMENT:
     226         [ +  + ]:       6052 :                     msg_deletedocument(message);
     227                 :       6050 :                     continue;
     228                 :            :                 case MSG_WRITEACCESS:
     229         [ +  - ]:        170 :                     msg_writeaccess(message);
     230                 :        170 :                     continue;
     231                 :            :                 case MSG_GETMETADATA:
     232         [ +  - ]:         46 :                     msg_getmetadata(message);
     233                 :         46 :                     continue;
     234                 :            :                 case MSG_SETMETADATA:
     235         [ +  - ]:         42 :                     msg_setmetadata(message);
     236                 :         42 :                     continue;
     237                 :            :                 case MSG_ADDSPELLING:
     238         [ +  - ]:         10 :                     msg_addspelling(message);
     239                 :         10 :                     continue;
     240                 :            :                 case MSG_REMOVESPELLING:
     241         [ +  - ]:         16 :                     msg_removespelling(message);
     242                 :         16 :                     continue;
     243                 :            :                 case MSG_METADATAKEYLIST:
     244         [ +  - ]:         16 :                     msg_metadatakeylist(message);
     245                 :         16 :                     continue;
     246                 :            :                 case MSG_FREQS:
     247         [ #  # ]:          0 :                     msg_freqs(message);
     248                 :          0 :                     continue;
     249                 :            :                 case MSG_UNIQUETERMS:
     250         [ +  + ]:       6156 :                     msg_uniqueterms(message);
     251                 :        144 :                     continue;
     252                 :            :                 case MSG_POSITIONLISTCOUNT:
     253         [ +  - ]:        102 :                     msg_positionlistcount(message);
     254                 :        102 :                     continue;
     255                 :            :                 default: {
     256                 :            :                     // MSG_GETMSET - used during a conversation.
     257                 :            :                     // MSG_SHUTDOWN - handled by get_message().
     258         [ #  # ]:          0 :                     string errmsg("Unexpected message type ");
     259 [ #  # ][ #  # ]:          0 :                     errmsg += str(type);
     260 [ #  # ][ #  # ]:          0 :                     throw Xapian::InvalidArgumentError(errmsg);
     261                 :            :                 }
     262                 :     325846 :             }
     263                 :          4 :         } catch (const Xapian::NetworkTimeoutError & e) {
     264                 :            :             try {
     265                 :            :                 // We've had a timeout, so the client may not be listening, so
     266                 :            :                 // set the end_time to 1 and if we can't send the message right
     267                 :            :                 // away, just exit and the client will cope.
     268 [ -  + ][ -  + ]:          2 :                 send_message(REPLY_EXCEPTION, serialise_error(e), 1.0);
     269         [ #  # ]:          0 :             } catch (...) {
     270                 :            :             }
     271                 :            :             // And rethrow it so our caller can log it and close the
     272                 :            :             // connection.
     273                 :          2 :             throw;
     274                 :          0 :         } catch (const Xapian::NetworkError &) {
     275                 :            :             // All other network errors mean we are fatally confused and are
     276                 :            :             // unlikely to be able to communicate further across this
     277                 :            :             // connection.  So we don't try to propagate the error to the
     278                 :            :             // client, but instead just rethrow the exception so our caller can
     279                 :            :             // log it and close the connection.
     280                 :          0 :             throw;
     281                 :      48372 :         } catch (const Xapian::Error &e) {
     282                 :            :             // Propagate the exception to the client, then return to the main
     283                 :            :             // message handling loop.
     284 [ -  + ][ -  + ]:      24186 :             send_message(REPLY_EXCEPTION, serialise_error(e));
     285                 :       1412 :         } catch (ConnectionClosed &) {
     286                 :       1412 :             return;
     287   [ +  -  +  +  :      24894 :         } catch (...) {
                      - ]
     288                 :            :             // Propagate an unknown exception to the client.
     289   [ #  #  #  # ]:          0 :             send_message(REPLY_EXCEPTION, string());
     290                 :            :             // And rethrow it so our caller can log it and close the
     291                 :            :             // connection.
     292                 :          0 :             throw;
     293                 :            :         }
     294                 :     325138 :     }
     295                 :            : }
     296                 :            : 
     297                 :            : void
     298                 :         90 : RemoteServer::msg_allterms(const string& message)
     299                 :            : {
     300         [ +  - ]:         90 :     string reply;
     301         [ +  - ]:        180 :     string prev = message;
     302                 :         90 :     const string& prefix = message;
     303         [ +  - ]:       1560 :     for (Xapian::TermIterator t = db->allterms_begin(prefix);
           [ +  -  +  + ]
     304                 :       1040 :          t != db->allterms_end(prefix);
     305                 :            :          ++t) {
     306         [ -  + ]:        430 :         if (rare(prev.size() > 255))
     307         [ #  # ]:          0 :             prev.resize(255);
     308         [ +  - ]:        430 :         const string& term = *t;
     309         [ +  - ]:        430 :         size_t reuse = common_prefix_length(prev, term);
     310 [ +  - ][ +  - ]:        430 :         reply += encode_length(t.get_termfreq());
                 [ +  - ]
     311         [ +  - ]:        430 :         reply.append(1, char(reuse));
     312 [ +  - ][ +  - ]:        430 :         reply += encode_length(term.size() - reuse);
     313         [ +  - ]:        430 :         reply.append(term, reuse, string::npos);
     314         [ +  - ]:        430 :         prev = term;
     315                 :        520 :     }
     316         [ +  - ]:        180 :     send_message(REPLY_ALLTERMS, reply);
     317                 :         90 : }
     318                 :            : 
     319                 :            : void
     320                 :       6878 : RemoteServer::msg_termlist(const string &message)
     321                 :            : {
     322                 :       6878 :     const char *p = message.data();
     323                 :       6878 :     const char *p_end = p + message.size();
     324                 :            :     Xapian::docid did;
     325         [ +  - ]:       6878 :     decode_length(&p, p_end, did);
     326         [ +  + ]:       6878 :     Xapian::TermIterator t = db->termlist_begin(did);
     327                 :        860 :     Xapian::termcount num_terms = 0;
     328         [ +  + ]:        860 :     if (t.internal)
     329         [ +  - ]:        842 :         num_terms = t.internal->get_approx_size();
     330 [ +  - ][ +  - ]:       1720 :     send_message(REPLY_TERMLIST0, encode_length(db->get_doclength(did)) +
         [ +  - ][ +  - ]
     331         [ +  - ]:        860 :                                   encode_length(num_terms));
     332         [ +  - ]:       1720 :     string reply;
     333         [ +  - ]:       1720 :     string prev;
     334         [ +  + ]:       5828 :     while (t != db->termlist_end(did)) {
     335         [ -  + ]:       4968 :         if (rare(prev.size() > 255))
     336         [ #  # ]:          0 :             prev.resize(255);
     337         [ +  - ]:       4968 :         const string& term = *t;
     338         [ +  - ]:       4968 :         size_t reuse = common_prefix_length(prev, term);
     339 [ +  - ][ +  - ]:       4968 :         reply += encode_length(t.get_wdf());
                 [ +  - ]
     340 [ +  - ][ +  - ]:       4968 :         reply += encode_length(t.get_termfreq());
                 [ +  - ]
     341         [ +  - ]:       4968 :         reply.append(1, char(reuse));
     342 [ +  - ][ +  - ]:       4968 :         reply += encode_length(term.size() - reuse);
     343         [ +  - ]:       4968 :         reply.append(term, reuse, string::npos);
     344         [ +  - ]:       4968 :         prev = term;
     345         [ +  - ]:       4968 :         ++t;
     346                 :       4968 :     }
     347         [ +  - ]:       1720 :     send_message(REPLY_TERMLIST, reply);
     348                 :        860 : }
     349                 :            : 
     350                 :            : void
     351                 :        612 : RemoteServer::msg_positionlist(const string &message)
     352                 :            : {
     353                 :        612 :     const char *p = message.data();
     354                 :        612 :     const char *p_end = p + message.size();
     355                 :            :     Xapian::docid did;
     356         [ +  - ]:        612 :     decode_length(&p, p_end, did);
     357         [ +  - ]:        612 :     string term(p, p_end - p);
     358                 :            : 
     359                 :        612 :     Xapian::termpos lastpos = static_cast<Xapian::termpos>(-1);
     360                 :       1224 :     const Xapian::PositionIterator end = db->positionlist_end(did, term);
     361 [ +  - ][ +  - ]:      16838 :     for (Xapian::PositionIterator i = db->positionlist_begin(did, term);
                 [ +  + ]
     362                 :            :          i != end; ++i) {
     363         [ +  - ]:      16226 :         Xapian::termpos pos = *i;
     364 [ +  - ][ +  - ]:      16226 :         send_message(REPLY_POSITIONLIST, encode_length(pos - lastpos - 1));
     365                 :      16226 :         lastpos = pos;
     366                 :        612 :     }
     367                 :            : 
     368 [ +  - ][ +  - ]:       1224 :     send_message(REPLY_DONE, string());
     369                 :        612 : }
     370                 :            : 
     371                 :            : void
     372                 :        102 : RemoteServer::msg_positionlistcount(const string &message)
     373                 :            : {
     374                 :        102 :     const char *p = message.data();
     375                 :        102 :     const char *p_end = p + message.size();
     376                 :            :     Xapian::docid did;
     377         [ +  - ]:        102 :     decode_length(&p, p_end, did);
     378                 :            : 
     379                 :            :     // This is kind of clumsy, but what the public API requires.
     380                 :        102 :     Xapian::termcount result = 0;
     381         [ +  - ]:        102 :     Xapian::TermIterator termit = db->termlist_begin(did);
     382         [ +  - ]:        102 :     if (termit != db->termlist_end(did)) {
     383         [ +  - ]:        102 :         string term(p, p_end - p);
     384         [ +  - ]:        102 :         termit.skip_to(term);
     385         [ +  - ]:        102 :         if (termit != db->termlist_end(did)) {
     386         [ +  - ]:        102 :             result = termit.positionlist_count();
     387                 :        102 :         }
     388                 :            :     }
     389 [ +  - ][ +  - ]:        102 :     send_message(REPLY_POSITIONLISTCOUNT, encode_length(result));
     390                 :        102 : }
     391                 :            : 
     392                 :            : void
     393                 :        262 : RemoteServer::msg_postlist(const string &message)
     394                 :            : {
     395                 :        262 :     const string & term = message;
     396                 :            : 
     397         [ +  - ]:        262 :     Xapian::doccount termfreq = db->get_termfreq(term);
     398         [ +  - ]:        262 :     Xapian::termcount collfreq = db->get_collection_freq(term);
     399 [ +  - ][ +  - ]:        262 :     send_message(REPLY_POSTLISTSTART, encode_length(termfreq) + encode_length(collfreq));
         [ +  - ][ +  - ]
     400                 :            : 
     401                 :        262 :     Xapian::docid lastdocid = 0;
     402                 :        262 :     const Xapian::PostingIterator end = db->postlist_end(term);
     403 [ +  - ][ +  - ]:       6834 :     for (Xapian::PostingIterator i = db->postlist_begin(term);
                 [ +  + ]
     404                 :            :          i != end; ++i) {
     405                 :            : 
     406         [ +  - ]:       6572 :         Xapian::docid newdocid = *i;
     407         [ +  - ]:       6572 :         string reply = encode_length(newdocid - lastdocid - 1);
     408 [ +  - ][ +  - ]:       6572 :         reply += encode_length(i.get_wdf());
                 [ +  - ]
     409                 :            : 
     410         [ +  - ]:       6572 :         send_message(REPLY_POSTLISTITEM, reply);
     411                 :       6572 :         lastdocid = newdocid;
     412                 :       6834 :     }
     413                 :            : 
     414 [ +  - ][ +  - ]:        262 :     send_message(REPLY_DONE, string());
     415                 :        262 : }
     416                 :            : 
     417                 :            : void
     418                 :        170 : RemoteServer::msg_writeaccess(const string & msg)
     419                 :            : {
     420         [ -  + ]:        170 :     if (!writable)
     421                 :          0 :         throw_read_only();
     422                 :            : 
     423                 :        170 :     int flags = Xapian::DB_OPEN;
     424                 :        170 :     const char *p = msg.c_str();
     425                 :        170 :     const char *p_end = p + msg.size();
     426         [ -  + ]:        170 :     if (p != p_end) {
     427                 :            :         unsigned flag_bits;
     428         [ #  # ]:          0 :         decode_length(&p, p_end, flag_bits);
     429                 :          0 :         flags |= flag_bits &~ Xapian::DB_ACTION_MASK_;
     430         [ #  # ]:          0 :         if (p != p_end) {
     431 [ #  # ][ #  # ]:          0 :             throw Xapian::NetworkError("Junk at end of MSG_WRITEACCESS");
                 [ #  # ]
     432                 :            :         }
     433                 :            :     }
     434                 :            : 
     435 [ +  - ][ +  - ]:        170 :     wdb = new Xapian::WritableDatabase(context, flags);
     436         [ +  - ]:        170 :     delete db;
     437                 :        170 :     db = wdb;
     438         [ +  - ]:        170 :     msg_update(msg);
     439                 :        170 : }
     440                 :            : 
     441                 :            : void
     442                 :         46 : RemoteServer::msg_reopen(const string & msg)
     443                 :            : {
     444         [ +  + ]:         46 :     if (!db->reopen()) {
     445         [ +  - ]:         36 :         send_message(REPLY_DONE, string());
     446                 :         46 :         return;
     447                 :            :     }
     448                 :         10 :     msg_update(msg);
     449                 :            : }
     450                 :            : 
     451                 :            : void
     452                 :       1114 : RemoteServer::msg_update(const string &)
     453                 :            : {
     454                 :            :     static const char protocol[2] = {
     455                 :            :         char(XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION),
     456                 :            :         char(XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION)
     457                 :            :     };
     458         [ +  - ]:       1114 :     string message(protocol, 2);
     459         [ +  - ]:       1114 :     Xapian::doccount num_docs = db->get_doccount();
     460 [ +  - ][ +  - ]:       1114 :     message += encode_length(num_docs);
     461 [ +  - ][ +  - ]:       1114 :     message += encode_length(db->get_lastdocid() - num_docs);
                 [ +  - ]
     462         [ +  - ]:       1114 :     Xapian::termcount doclen_lb = db->get_doclength_lower_bound();
     463 [ +  - ][ +  - ]:       1114 :     message += encode_length(doclen_lb);
     464 [ +  - ][ +  - ]:       1114 :     message += encode_length(db->get_doclength_upper_bound() - doclen_lb);
                 [ +  - ]
     465 [ +  - ][ +  + ]:       1114 :     message += (db->has_positions() ? '1' : '0');
                 [ +  - ]
     466 [ +  - ][ +  - ]:       1114 :     message += encode_length(db->get_total_length());
                 [ +  - ]
     467         [ +  - ]:       2228 :     string uuid = db->get_uuid();
     468         [ +  - ]:       1114 :     message += uuid;
     469         [ +  - ]:       2228 :     send_message(REPLY_UPDATE, message);
     470                 :       1114 : }
     471                 :            : 
     472                 :            : void
     473                 :      10362 : RemoteServer::msg_query(const string &message_in)
     474                 :            : {
     475                 :      10362 :     const char *p = message_in.c_str();
     476                 :      10362 :     const char *p_end = p + message_in.size();
     477                 :            : 
     478                 :            :     // Unserialise the Query.
     479                 :            :     size_t len;
     480         [ +  - ]:      10362 :     decode_length_and_check(&p, p_end, len);
     481 [ +  - ][ +  - ]:      10362 :     Xapian::Query query(Xapian::Query::unserialise(string(p, len), reg));
     482                 :      10362 :     p += len;
     483                 :            : 
     484                 :            :     // Unserialise assorted Enquire settings.
     485                 :            :     Xapian::termcount qlen;
     486         [ +  - ]:      10362 :     decode_length(&p, p_end, qlen);
     487                 :            : 
     488                 :            :     Xapian::valueno collapse_max;
     489         [ +  - ]:      10362 :     decode_length(&p, p_end, collapse_max);
     490                 :            : 
     491                 :      10362 :     Xapian::valueno collapse_key = Xapian::BAD_VALUENO;
     492         [ +  + ]:      10362 :     if (collapse_max)
     493         [ +  - ]:       5080 :         decode_length(&p, p_end, collapse_key);
     494                 :            : 
     495 [ +  - ][ +  - ]:      10362 :     if (p_end - p < 4 || *p < '0' || *p > '2') {
                 [ -  + ]
     496 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (docid_order)");
                 [ #  # ]
     497                 :            :     }
     498                 :            :     Xapian::Enquire::docid_order order;
     499                 :      10362 :     order = static_cast<Xapian::Enquire::docid_order>(*p++ - '0');
     500                 :            : 
     501                 :            :     Xapian::valueno sort_key;
     502         [ +  - ]:      10362 :     decode_length(&p, p_end, sort_key);
     503                 :            : 
     504 [ +  - ][ -  + ]:      10362 :     if (*p < '0' || *p > '3') {
     505 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (sort_by)");
                 [ #  # ]
     506                 :            :     }
     507                 :            :     Xapian::Enquire::Internal::sort_setting sort_by;
     508                 :      10362 :     sort_by = static_cast<Xapian::Enquire::Internal::sort_setting>(*p++ - '0');
     509                 :            : 
     510 [ +  - ][ -  + ]:      10362 :     if (*p < '0' || *p > '1') {
     511 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (sort_value_forward)");
                 [ #  # ]
     512                 :            :     }
     513                 :      10362 :     bool sort_value_forward(*p++ != '0');
     514                 :            : 
     515         [ +  - ]:      10362 :     double time_limit = unserialise_double(&p, p_end);
     516                 :            : 
     517                 :      10362 :     int percent_threshold = *p++;
     518 [ +  - ][ -  + ]:      10362 :     if (percent_threshold < 0 || percent_threshold > 100) {
     519 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (percent_threshold)");
                 [ #  # ]
     520                 :            :     }
     521                 :            : 
     522         [ +  - ]:      10362 :     double weight_threshold = unserialise_double(&p, p_end);
     523         [ -  + ]:      10362 :     if (weight_threshold < 0) {
     524 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (weight_threshold)");
                 [ #  # ]
     525                 :            :     }
     526                 :            : 
     527                 :            :     // Unserialise the Weight object.
     528         [ +  - ]:      10362 :     decode_length_and_check(&p, p_end, len);
     529         [ +  - ]:      20724 :     string wtname(p, len);
     530                 :      10362 :     p += len;
     531                 :            : 
     532         [ +  - ]:      10362 :     const Xapian::Weight * wttype = reg.get_weighting_scheme(wtname);
     533         [ -  + ]:      10362 :     if (wttype == NULL) {
     534                 :            :         // Note: user weighting schemes should be registered by adding them to
     535                 :            :         // a Registry, and setting the context using
     536                 :            :         // RemoteServer::set_registry().
     537         [ #  # ]:          0 :         throw Xapian::InvalidArgumentError("Weighting scheme " +
     538 [ #  # ][ #  # ]:          0 :                                            wtname + " not registered");
                 [ #  # ]
     539                 :            :     }
     540                 :            : 
     541         [ +  - ]:      10362 :     decode_length_and_check(&p, p_end, len);
     542 [ +  - ][ +  - ]:      20724 :     unique_ptr<Xapian::Weight> wt(wttype->unserialise(string(p, len)));
     543                 :      10362 :     p += len;
     544                 :            : 
     545                 :            :     // Unserialise the RSet object.
     546         [ +  - ]:      10362 :     decode_length_and_check(&p, p_end, len);
     547 [ +  - ][ +  - ]:      20724 :     Xapian::RSet rset = unserialise_rset(string(p, len));
     548                 :      10362 :     p += len;
     549                 :            : 
     550                 :            :     // Unserialise any MatchSpy objects.
     551                 :      20724 :     vector<Xapian::Internal::opt_intrusive_ptr<Xapian::MatchSpy>> matchspies;
     552         [ +  + ]:      10366 :     while (p != p_end) {
     553         [ +  - ]:          4 :         decode_length_and_check(&p, p_end, len);
     554         [ +  - ]:          4 :         string spytype(p, len);
     555         [ +  - ]:          4 :         const Xapian::MatchSpy * spyclass = reg.get_match_spy(spytype);
     556         [ -  + ]:          4 :         if (spyclass == NULL) {
     557 [ #  # ][ #  # ]:          0 :             throw Xapian::InvalidArgumentError("Match spy " + spytype +
     558 [ #  # ][ #  # ]:          0 :                                                " not registered");
     559                 :            :         }
     560                 :          4 :         p += len;
     561                 :            : 
     562         [ +  - ]:          4 :         decode_length_and_check(&p, p_end, len);
     563 [ +  - ][ +  - ]:          4 :         matchspies.push_back(spyclass->unserialise(string(p, len), reg)->release());
                 [ +  - ]
     564                 :          4 :         p += len;
     565                 :          4 :     }
     566                 :            : 
     567         [ +  - ]:      20724 :     Xapian::Weight::Internal local_stats;
     568                 :      10362 :     Matcher matcher(*db, query, qlen, &rset, local_stats, *wt,
     569                 :            :                     false, false,
     570                 :            :                     collapse_key, collapse_max,
     571                 :            :                     percent_threshold, weight_threshold,
     572                 :            :                     order, sort_key, sort_by, sort_value_forward, time_limit,
     573         [ +  - ]:      20724 :                     matchspies);
     574                 :            : 
     575 [ +  - ][ +  - ]:      10362 :     send_message(REPLY_STATS, serialise_stats(local_stats));
     576                 :            : 
     577         [ +  - ]:      20724 :     string message;
     578         [ +  - ]:      10362 :     get_message(active_timeout, message, MSG_GETMSET);
     579                 :      10362 :     p = message.c_str();
     580                 :      10362 :     p_end = p + message.size();
     581                 :            : 
     582                 :            :     Xapian::termcount first;
     583         [ +  - ]:      10362 :     decode_length(&p, p_end, first);
     584                 :            :     Xapian::termcount maxitems;
     585         [ +  - ]:      10362 :     decode_length(&p, p_end, maxitems);
     586                 :            : 
     587                 :            :     Xapian::termcount check_at_least;
     588         [ +  - ]:      10362 :     decode_length(&p, p_end, check_at_least);
     589                 :            : 
     590         [ +  - ]:      10362 :     message.erase(0, message.size() - (p_end - p));
     591 [ +  - ][ +  - ]:      20724 :     unique_ptr<Xapian::Weight::Internal> total_stats(new Xapian::Weight::Internal);
     592         [ +  - ]:      10362 :     unserialise_stats(message, *total_stats);
     593         [ +  - ]:      10362 :     total_stats->set_bounds_from_db(*db);
     594                 :            : 
     595                 :            :     Xapian::MSet mset = matcher.get_mset(first, maxitems, check_at_least,
     596                 :      10362 :                                          *total_stats, *wt, 0, 0,
     597                 :            :                                          collapse_key, collapse_max,
     598                 :            :                                          percent_threshold, weight_threshold,
     599                 :            :                                          order,
     600                 :            :                                          sort_key, sort_by, sort_value_forward,
     601         [ +  + ]:      20712 :                                          time_limit, matchspies);
     602                 :            :     // FIXME: The local side already has these stats, except for the maxpart
     603                 :            :     // information.
     604                 :      10350 :     mset.internal->set_stats(total_stats.release());
     605                 :            : 
     606         [ +  - ]:      10350 :     message.resize(0);
     607         [ +  + ]:      10354 :     for (auto i : matchspies) {
     608         [ +  - ]:          8 :         string spy_results = i->serialise_results();
     609 [ +  - ][ +  - ]:          4 :         message += encode_length(spy_results.size());
     610         [ +  - ]:          4 :         message += spy_results;
     611                 :          4 :     }
     612 [ +  - ][ +  - ]:      10350 :     message += mset.internal->serialise();
     613         [ +  - ]:      20712 :     send_message(REPLY_RESULTS, message);
     614                 :      10350 : }
     615                 :            : 
     616                 :            : void
     617                 :     235854 : RemoteServer::msg_document(const string &message)
     618                 :            : {
     619                 :     235854 :     const char *p = message.data();
     620                 :     235854 :     const char *p_end = p + message.size();
     621                 :            :     Xapian::docid did;
     622         [ +  - ]:     235854 :     decode_length(&p, p_end, did);
     623                 :            : 
     624         [ +  + ]:     235854 :     Xapian::Document doc = db->get_document(did);
     625                 :            : 
     626 [ +  - ][ +  - ]:     229822 :     send_message(REPLY_DOCDATA, doc.get_data());
     627                 :            : 
     628                 :     459644 :     Xapian::ValueIterator i;
     629 [ +  - ][ +  - ]:    1928248 :     for (i = doc.values_begin(); i != doc.values_end(); ++i) {
         [ +  - ][ +  + ]
     630 [ +  - ][ +  - ]:    1698426 :         string item = encode_length(i.get_valueno());
     631 [ +  - ][ +  - ]:    1698426 :         item += *i;
     632         [ +  - ]:    1698426 :         send_message(REPLY_VALUE, item);
     633                 :    1698426 :     }
     634 [ +  - ][ +  - ]:     459644 :     send_message(REPLY_DONE, string());
     635                 :     229822 : }
     636                 :            : 
     637                 :            : void
     638                 :         20 : RemoteServer::msg_keepalive(const string &)
     639                 :            : {
     640                 :            :     // Ensure *our* database stays alive, as it may contain remote databases!
     641                 :         20 :     db->keep_alive();
     642         [ +  - ]:         20 :     send_message(REPLY_DONE, string());
     643                 :         20 : }
     644                 :            : 
     645                 :            : void
     646                 :        138 : RemoteServer::msg_termexists(const string &term)
     647                 :            : {
     648 [ +  - ][ +  + ]:        138 :     send_message((db->term_exists(term) ? REPLY_TERMEXISTS : REPLY_TERMDOESNTEXIST), string());
                 [ +  - ]
     649                 :        138 : }
     650                 :            : 
     651                 :            : void
     652                 :       2852 : RemoteServer::msg_collfreq(const string &term)
     653                 :            : {
     654         [ +  - ]:       2852 :     send_message(REPLY_COLLFREQ, encode_length(db->get_collection_freq(term)));
     655                 :       2852 : }
     656                 :            : 
     657                 :            : void
     658                 :        838 : RemoteServer::msg_termfreq(const string &term)
     659                 :            : {
     660         [ +  - ]:        838 :     send_message(REPLY_TERMFREQ, encode_length(db->get_termfreq(term)));
     661                 :        838 : }
     662                 :            : 
     663                 :            : void
     664                 :          0 : RemoteServer::msg_freqs(const string &term)
     665                 :            : {
     666 [ #  # ][ #  # ]:          0 :     string msg = encode_length(db->get_termfreq(term));
     667 [ #  # ][ #  # ]:          0 :     msg += encode_length(db->get_collection_freq(term));
                 [ #  # ]
     668         [ #  # ]:          0 :     send_message(REPLY_FREQS, msg);
     669                 :          0 : }
     670                 :            : 
     671                 :            : void
     672                 :        108 : RemoteServer::msg_valuestats(const string & message)
     673                 :            : {
     674                 :        108 :     const char *p = message.data();
     675                 :        108 :     const char *p_end = p + message.size();
     676         [ +  + ]:        216 :     while (p != p_end) {
     677                 :            :         Xapian::valueno slot;
     678         [ +  - ]:        108 :         decode_length(&p, p_end, slot);
     679         [ +  - ]:        108 :         string message_out;
     680 [ +  - ][ +  - ]:        108 :         message_out += encode_length(db->get_value_freq(slot));
                 [ +  - ]
     681         [ +  - ]:        216 :         string bound = db->get_value_lower_bound(slot);
     682 [ +  - ][ +  - ]:        108 :         message_out += encode_length(bound.size());
     683         [ +  - ]:        108 :         message_out += bound;
     684 [ +  - ][ +  - ]:        108 :         bound = db->get_value_upper_bound(slot);
     685 [ +  - ][ +  - ]:        108 :         message_out += encode_length(bound.size());
     686         [ +  - ]:        108 :         message_out += bound;
     687                 :            : 
     688         [ +  - ]:        108 :         send_message(REPLY_VALUESTATS, message_out);
     689                 :        108 :     }
     690                 :        108 : }
     691                 :            : 
     692                 :            : void
     693                 :       6268 : RemoteServer::msg_doclength(const string &message)
     694                 :            : {
     695                 :       6268 :     const char *p = message.data();
     696                 :       6268 :     const char *p_end = p + message.size();
     697                 :            :     Xapian::docid did;
     698         [ +  - ]:       6268 :     decode_length(&p, p_end, did);
     699 [ +  + ][ +  - ]:       6268 :     send_message(REPLY_DOCLENGTH, encode_length(db->get_doclength(did)));
                 [ +  - ]
     700                 :        254 : }
     701                 :            : 
     702                 :            : void
     703                 :       6156 : RemoteServer::msg_uniqueterms(const string &message)
     704                 :            : {
     705                 :       6156 :     const char *p = message.data();
     706                 :       6156 :     const char *p_end = p + message.size();
     707                 :            :     Xapian::docid did;
     708         [ +  - ]:       6156 :     decode_length(&p, p_end, did);
     709 [ +  + ][ +  - ]:       6156 :     send_message(REPLY_UNIQUETERMS, encode_length(db->get_unique_terms(did)));
                 [ +  - ]
     710                 :        144 : }
     711                 :            : 
     712                 :            : void
     713                 :       4360 : RemoteServer::msg_commit(const string &)
     714                 :            : {
     715         [ -  + ]:       4360 :     if (!wdb)
     716                 :          0 :         throw_read_only();
     717                 :            : 
     718                 :       4360 :     wdb->commit();
     719                 :            : 
     720         [ +  - ]:       4356 :     send_message(REPLY_DONE, string());
     721                 :       4356 : }
     722                 :            : 
     723                 :            : void
     724                 :         14 : RemoteServer::msg_cancel(const string &)
     725                 :            : {
     726         [ -  + ]:         14 :     if (!wdb)
     727                 :          0 :         throw_read_only();
     728                 :            : 
     729                 :            :     // We can't call cancel since that's an internal method, but this
     730                 :            :     // has the same effect with minimal additional overhead.
     731                 :         14 :     wdb->begin_transaction(false);
     732                 :         14 :     wdb->cancel_transaction();
     733                 :         14 : }
     734                 :            : 
     735                 :            : void
     736                 :      37432 : RemoteServer::msg_adddocument(const string & message)
     737                 :            : {
     738         [ -  + ]:      37432 :     if (!wdb)
     739                 :          0 :         throw_read_only();
     740                 :            : 
     741         [ +  + ]:      37432 :     Xapian::docid did = wdb->add_document(unserialise_document(message));
     742                 :            : 
     743         [ +  - ]:      37340 :     send_message(REPLY_ADDDOCUMENT, encode_length(did));
     744                 :      37340 : }
     745                 :            : 
     746                 :            : void
     747                 :       6052 : RemoteServer::msg_deletedocument(const string & message)
     748                 :            : {
     749         [ -  + ]:       6052 :     if (!wdb)
     750                 :          0 :         throw_read_only();
     751                 :            : 
     752                 :       6052 :     const char *p = message.data();
     753                 :       6052 :     const char *p_end = p + message.size();
     754                 :            :     Xapian::docid did;
     755         [ +  - ]:       6052 :     decode_length(&p, p_end, did);
     756                 :            : 
     757         [ +  + ]:       6052 :     wdb->delete_document(did);
     758                 :            : 
     759 [ +  - ][ +  - ]:       6050 :     send_message(REPLY_DONE, string());
     760                 :       6050 : }
     761                 :            : 
     762                 :            : void
     763                 :          6 : RemoteServer::msg_deletedocumentterm(const string & message)
     764                 :            : {
     765         [ -  + ]:          6 :     if (!wdb)
     766                 :          0 :         throw_read_only();
     767                 :            : 
     768                 :          6 :     wdb->delete_document(message);
     769                 :          6 : }
     770                 :            : 
     771                 :            : void
     772                 :       6124 : RemoteServer::msg_replacedocument(const string & message)
     773                 :            : {
     774         [ -  + ]:       6124 :     if (!wdb)
     775                 :          0 :         throw_read_only();
     776                 :            : 
     777                 :       6124 :     const char *p = message.data();
     778                 :       6124 :     const char *p_end = p + message.size();
     779                 :            :     Xapian::docid did;
     780         [ +  - ]:       6124 :     decode_length(&p, p_end, did);
     781                 :            : 
     782 [ +  - ][ +  - ]:       6124 :     wdb->replace_document(did, unserialise_document(string(p, p_end)));
                 [ +  - ]
     783                 :       6124 : }
     784                 :            : 
     785                 :            : void
     786                 :         38 : RemoteServer::msg_replacedocumentterm(const string & message)
     787                 :            : {
     788         [ -  + ]:         38 :     if (!wdb)
     789                 :          0 :         throw_read_only();
     790                 :            : 
     791                 :         38 :     const char *p = message.data();
     792                 :         38 :     const char *p_end = p + message.size();
     793                 :            :     size_t len;
     794         [ +  - ]:         38 :     decode_length_and_check(&p, p_end, len);
     795         [ +  - ]:         38 :     string unique_term(p, len);
     796                 :         38 :     p += len;
     797                 :            : 
     798 [ +  - ][ +  - ]:         38 :     Xapian::docid did = wdb->replace_document(unique_term, unserialise_document(string(p, p_end)));
                 [ +  - ]
     799                 :            : 
     800 [ +  - ][ +  - ]:         38 :     send_message(REPLY_ADDDOCUMENT, encode_length(did));
     801                 :         38 : }
     802                 :            : 
     803                 :            : void
     804                 :         46 : RemoteServer::msg_getmetadata(const string & message)
     805                 :            : {
     806         [ +  - ]:         46 :     send_message(REPLY_METADATA, db->get_metadata(message));
     807                 :         46 : }
     808                 :            : 
     809                 :            : void
     810                 :         16 : RemoteServer::msg_metadatakeylist(const string& message)
     811                 :            : {
     812         [ +  - ]:         16 :     string reply;
     813         [ +  - ]:         32 :     string prev = message;
     814                 :         16 :     const string& prefix = message;
     815         [ +  - ]:        144 :     for (Xapian::TermIterator t = db->metadata_keys_begin(prefix);
           [ +  -  +  + ]
     816                 :         96 :          t != db->metadata_keys_end(prefix);
     817                 :            :          ++t) {
     818         [ -  + ]:         32 :         if (rare(prev.size() > 255))
     819         [ #  # ]:          0 :             prev.resize(255);
     820         [ +  - ]:         32 :         const string& term = *t;
     821         [ +  - ]:         32 :         size_t reuse = common_prefix_length(prev, term);
     822         [ +  - ]:         32 :         reply.append(1, char(reuse));
     823 [ +  - ][ +  - ]:         32 :         reply += encode_length(term.size() - reuse);
     824         [ +  - ]:         32 :         reply.append(term, reuse, string::npos);
     825         [ +  - ]:         32 :         prev = term;
     826                 :         48 :     }
     827         [ +  - ]:         32 :     send_message(REPLY_METADATAKEYLIST, reply);
     828                 :         16 : }
     829                 :            : 
     830                 :            : void
     831                 :         42 : RemoteServer::msg_setmetadata(const string & message)
     832                 :            : {
     833         [ -  + ]:         42 :     if (!wdb)
     834                 :          0 :         throw_read_only();
     835                 :         42 :     const char *p = message.data();
     836                 :         42 :     const char *p_end = p + message.size();
     837                 :            :     size_t keylen;
     838         [ +  - ]:         42 :     decode_length_and_check(&p, p_end, keylen);
     839         [ +  - ]:         42 :     string key(p, keylen);
     840                 :         42 :     p += keylen;
     841         [ +  - ]:         84 :     string val(p, p_end - p);
     842         [ +  - ]:         84 :     wdb->set_metadata(key, val);
     843                 :         42 : }
     844                 :            : 
     845                 :            : void
     846                 :         10 : RemoteServer::msg_addspelling(const string & message)
     847                 :            : {
     848         [ -  + ]:         10 :     if (!wdb)
     849                 :          0 :         throw_read_only();
     850                 :         10 :     const char *p = message.data();
     851                 :         10 :     const char *p_end = p + message.size();
     852                 :            :     Xapian::termcount freqinc;
     853         [ +  - ]:         10 :     decode_length(&p, p_end, freqinc);
     854 [ +  - ][ +  - ]:         10 :     wdb->add_spelling(string(p, p_end - p), freqinc);
     855                 :         10 : }
     856                 :            : 
     857                 :            : void
     858                 :         16 : RemoteServer::msg_removespelling(const string & message)
     859                 :            : {
     860         [ -  + ]:         16 :     if (!wdb)
     861                 :          0 :         throw_read_only();
     862                 :         16 :     const char *p = message.data();
     863                 :         16 :     const char *p_end = p + message.size();
     864                 :            :     Xapian::termcount freqdec;
     865         [ +  - ]:         16 :     decode_length(&p, p_end, freqdec);
     866 [ +  - ][ +  - ]:         16 :     auto result = wdb->remove_spelling(string(p, p_end - p), freqdec);
     867 [ +  - ][ +  - ]:         16 :     send_message(REPLY_REMOVESPELLING, encode_length(result));
     868                 :         16 : }

Generated by: LCOV version 1.11