LCOV - code coverage report
Current view: top level - net - remoteserver.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 954b5873a738 Lines: 442 526 84.0 %
Date: 2019-06-30 05:20:33 Functions: 34 36 94.4 %
Branches: 401 994 40.3 %

           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,2019 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 "matcher/matcher.h"
      42                 :            : #include "omassert.h"
      43                 :            : #include "pack.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                 :        774 : 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 [ +  - ][ +  - ]:        774 :       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 [ +  - ][ +  - ]:        774 :         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         [ +  - ]:        774 :         context = dbpaths[0];
      83                 :            : 
      84         [ +  + ]:        774 :         if (!writable) {
      85                 :        600 :             vector<string>::const_iterator i(dbpaths.begin());
      86         [ -  + ]:        600 :             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         [ -  + ]:        774 :     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 [ +  - ][ +  - ]:        774 :     msg_update(string());
     110                 :        774 : }
     111                 :            : 
     112                 :       1548 : RemoteServer::~RemoteServer()
     113                 :            : {
     114         [ +  - ]:        774 :     delete db;
     115                 :            :     // wdb is either NULL or equal to db, so we shouldn't delete it too!
     116                 :        774 : }
     117                 :            : 
     118                 :            : message_type
     119                 :     337332 : RemoteServer::get_message(double timeout, string & result,
     120                 :            :                           message_type required_type)
     121                 :            : {
     122                 :     337332 :     double end_time = RealTime::end_time(timeout);
     123                 :     337332 :     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 [ +  + ][ +  + ]:     337330 :     if (type == MSG_SHUTDOWN || (type < 0 && wdb == NULL))
                 [ +  - ]
     129                 :        772 :         throw ConnectionClosed();
     130         [ -  + ]:     336558 :     if (type < 0)
     131 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Connection closed unexpectedly");
                 [ #  # ]
     132         [ -  + ]:     336558 :     if (type >= MSG_MAX) {
     133         [ #  # ]:          0 :         string errmsg("Invalid message type ");
     134 [ #  # ][ #  # ]:          0 :         errmsg += str(type);
     135 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError(errmsg);
     136                 :            :     }
     137 [ +  + ][ -  + ]:     336558 :     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                 :     336558 :     return static_cast<message_type>(type);
     145                 :            : }
     146                 :            : 
     147                 :            : void
     148                 :    2268132 : RemoteServer::send_message(reply_type type, const string &message)
     149                 :            : {
     150                 :    2268132 :     double end_time = RealTime::end_time(active_timeout);
     151                 :    2268132 :     unsigned char type_as_char = static_cast<unsigned char>(type);
     152                 :    2268132 :     RemoteConnection::send_message(type_as_char, message, end_time);
     153                 :    2268132 : }
     154                 :            : 
     155                 :            : typedef void (RemoteServer::* dispatch_func)(const string &);
     156                 :            : 
     157                 :            : void
     158                 :        774 : RemoteServer::run()
     159                 :            : {
     160                 :            :     while (true) {
     161                 :            :         try {
     162         [ +  - ]:     326674 :             string message;
     163         [ +  + ]:     326674 :             size_t type = get_message(idle_timeout, message);
     164   [ +  +  +  +  :     325900 :             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         [ +  + ]:     235992 :                     msg_document(message);
     173                 :     229960 :                     continue;
     174                 :            :                 case MSG_TERMEXISTS:
     175         [ +  - ]:        142 :                     msg_termexists(message);
     176                 :        142 :                     continue;
     177                 :            :                 case MSG_TERMFREQ:
     178         [ +  - ]:       1016 :                     msg_termfreq(message);
     179                 :       1016 :                     continue;
     180                 :            :                 case MSG_VALUESTATS:
     181         [ +  - ]:        110 :                     msg_valuestats(message);
     182                 :        110 :                     continue;
     183                 :            :                 case MSG_KEEPALIVE:
     184         [ +  - ]:         20 :                     msg_keepalive(message);
     185                 :         20 :                     continue;
     186                 :            :                 case MSG_DOCLENGTH:
     187         [ +  + ]:       6272 :                     msg_doclength(message);
     188                 :        258 :                     continue;
     189                 :            :                 case MSG_QUERY:
     190         [ +  + ]:      10658 :                     msg_query(message);
     191                 :      10638 :                     continue;
     192                 :            :                 case MSG_TERMLIST:
     193         [ +  + ]:       6994 :                     msg_termlist(message);
     194                 :        976 :                     continue;
     195                 :            :                 case MSG_POSITIONLIST:
     196         [ +  - ]:        614 :                     msg_positionlist(message);
     197                 :        614 :                     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         [ +  - ]:        228 :                     msg_update(message);
     206                 :        228 :                     continue;
     207                 :            :                 case MSG_ADDDOCUMENT:
     208         [ +  + ]:      37434 :                     msg_adddocument(message);
     209                 :      37342 :                     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         [ +  + ]:       4366 :                     msg_commit(message);
     218                 :       4362 :                     continue;
     219                 :            :                 case MSG_REPLACEDOCUMENT:
     220         [ +  + ]:       6128 :                     msg_replacedocument(message);
     221                 :       6126 :                     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         [ +  - ]:        174 :                     msg_writeaccess(message);
     230                 :        174 :                     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         [ +  + ]:       6160 :                     msg_uniqueterms(message);
     251                 :        148 :                     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                 :     326674 :             }
     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                 :      48392 :         } catch (const Xapian::Error &e) {
     282                 :            :             // Propagate the exception to the client, then return to the main
     283                 :            :             // message handling loop.
     284 [ -  + ][ -  + ]:      24196 :             send_message(REPLY_EXCEPTION, serialise_error(e));
     285                 :       1544 :         } catch (ConnectionClosed &) {
     286                 :       1544 :             return;
     287   [ +  -  +  +  :      24970 :         } 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                 :     325900 :     }
     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.append(1, char(reuse));
     311         [ +  - ]:        430 :         pack_uint(reply, term.size() - reuse);
     312         [ +  - ]:        430 :         reply.append(term, reuse, string::npos);
     313 [ +  - ][ +  - ]:        430 :         pack_uint(reply, t.get_termfreq());
     314         [ +  - ]:        430 :         prev = term;
     315                 :        520 :     }
     316         [ +  - ]:        180 :     send_message(REPLY_ALLTERMS, reply);
     317                 :         90 : }
     318                 :            : 
     319                 :            : void
     320                 :       6994 : RemoteServer::msg_termlist(const string &message)
     321                 :            : {
     322                 :       6994 :     const char *p = message.data();
     323                 :       6994 :     const char *p_end = p + message.size();
     324                 :            :     Xapian::docid did;
     325         [ -  + ]:       6994 :     if (!unpack_uint_last(&p, p_end, &did)) {
     326 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_TERMLIST");
                 [ #  # ]
     327                 :            :     }
     328         [ +  + ]:       6994 :     Xapian::TermIterator t = db->termlist_begin(did);
     329                 :        976 :     Xapian::termcount num_terms = 0;
     330         [ +  + ]:        976 :     if (t.internal)
     331         [ +  - ]:        958 :         num_terms = t.internal->get_approx_size();
     332         [ +  - ]:       1952 :     string reply;
     333 [ +  - ][ +  - ]:        976 :     pack_uint(reply, db->get_doclength(did));
     334         [ +  - ]:        976 :     pack_uint_last(reply, num_terms);
     335         [ +  - ]:        976 :     send_message(REPLY_TERMLISTHEADER, reply);
     336                 :            : 
     337         [ +  - ]:        976 :     reply.resize(0);
     338         [ +  - ]:       1952 :     string prev;
     339         [ +  + ]:       7684 :     while (t != db->termlist_end(did)) {
     340         [ -  + ]:       6708 :         if (rare(prev.size() > 255))
     341         [ #  # ]:          0 :             prev.resize(255);
     342         [ +  - ]:       6708 :         const string& term = *t;
     343         [ +  - ]:       6708 :         size_t reuse = common_prefix_length(prev, term);
     344         [ +  - ]:       6708 :         reply.append(1, char(reuse));
     345         [ +  - ]:       6708 :         pack_uint(reply, term.size() - reuse);
     346         [ +  - ]:       6708 :         reply.append(term, reuse, string::npos);
     347 [ +  - ][ +  - ]:       6708 :         pack_uint(reply, t.get_wdf());
     348 [ +  - ][ +  - ]:       6708 :         pack_uint(reply, t.get_termfreq());
     349         [ +  - ]:       6708 :         prev = term;
     350         [ +  - ]:       6708 :         ++t;
     351                 :       6708 :     }
     352         [ +  - ]:       1952 :     send_message(REPLY_TERMLIST, reply);
     353                 :        976 : }
     354                 :            : 
     355                 :            : void
     356                 :        614 : RemoteServer::msg_positionlist(const string &message)
     357                 :            : {
     358                 :        614 :     const char *p = message.data();
     359                 :        614 :     const char *p_end = p + message.size();
     360                 :            :     Xapian::docid did;
     361         [ -  + ]:        614 :     if (!unpack_uint(&p, p_end, &did)) {
     362 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_POSITIONLIST");
                 [ #  # ]
     363                 :            :     }
     364         [ +  - ]:        614 :     string term(p, p_end - p);
     365                 :            : 
     366         [ +  - ]:       1228 :     string reply;
     367                 :        614 :     Xapian::termpos lastpos = static_cast<Xapian::termpos>(-1);
     368         [ +  - ]:      50520 :     for (Xapian::PositionIterator i = db->positionlist_begin(did, term);
           [ +  -  +  + ]
     369                 :      33680 :          i != db->positionlist_end(did, term);
     370                 :            :          ++i) {
     371         [ +  - ]:      16226 :         Xapian::termpos pos = *i;
     372         [ +  - ]:      16226 :         pack_uint(reply, pos - lastpos - 1);
     373                 :      16226 :         lastpos = pos;
     374                 :        614 :     }
     375         [ +  - ]:       1228 :     send_message(REPLY_POSITIONLIST, reply);
     376                 :        614 : }
     377                 :            : 
     378                 :            : void
     379                 :        102 : RemoteServer::msg_positionlistcount(const string &message)
     380                 :            : {
     381                 :        102 :     const char *p = message.data();
     382                 :        102 :     const char *p_end = p + message.size();
     383                 :            :     Xapian::docid did;
     384         [ -  + ]:        102 :     if (!unpack_uint(&p, p_end, &did)) {
     385 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_POSITIONLISTCOUNT");
                 [ #  # ]
     386                 :            :     }
     387                 :            : 
     388                 :            :     // This is kind of clumsy, but what the public API requires.
     389                 :        102 :     Xapian::termcount result = 0;
     390         [ +  - ]:        102 :     Xapian::TermIterator termit = db->termlist_begin(did);
     391         [ +  - ]:        102 :     if (termit != db->termlist_end(did)) {
     392         [ +  - ]:        102 :         string term(p, p_end - p);
     393         [ +  - ]:        102 :         termit.skip_to(term);
     394         [ +  - ]:        102 :         if (termit != db->termlist_end(did)) {
     395         [ +  - ]:        102 :             result = termit.positionlist_count();
     396                 :        102 :         }
     397                 :            :     }
     398         [ +  - ]:        204 :     string reply;
     399         [ +  - ]:        102 :     pack_uint_last(reply, result);
     400         [ +  - ]:        204 :     send_message(REPLY_POSITIONLISTCOUNT, reply);
     401                 :        102 : }
     402                 :            : 
     403                 :            : void
     404                 :        262 : RemoteServer::msg_postlist(const string &message)
     405                 :            : {
     406                 :        262 :     const string & term = message;
     407                 :            : 
     408         [ +  - ]:        262 :     Xapian::doccount termfreq = db->get_termfreq(term);
     409         [ +  - ]:        262 :     string reply;
     410         [ +  - ]:        262 :     pack_uint_last(reply, termfreq);
     411         [ +  - ]:        262 :     send_message(REPLY_POSTLISTHEADER, reply);
     412                 :            : 
     413         [ +  - ]:        262 :     reply.resize(0);
     414                 :        262 :     Xapian::docid lastdocid = 0;
     415         [ +  - ]:      20502 :     for (Xapian::PostingIterator i = db->postlist_begin(term);
           [ +  -  +  + ]
     416                 :      13668 :          i != db->postlist_end(term);
     417                 :            :          ++i) {
     418         [ +  - ]:       6572 :         Xapian::docid newdocid = *i;
     419         [ +  - ]:       6572 :         pack_uint(reply, newdocid - lastdocid - 1);
     420 [ +  - ][ +  - ]:       6572 :         pack_uint(reply, i.get_wdf());
     421                 :            : 
     422                 :       6572 :         lastdocid = newdocid;
     423                 :        262 :     }
     424                 :            : 
     425         [ +  - ]:        262 :     send_message(REPLY_POSTLIST, reply);
     426                 :        262 : }
     427                 :            : 
     428                 :            : void
     429                 :        174 : RemoteServer::msg_writeaccess(const string & msg)
     430                 :            : {
     431         [ -  + ]:        174 :     if (!writable)
     432                 :          0 :         throw_read_only();
     433                 :            : 
     434                 :        174 :     int flags = Xapian::DB_OPEN;
     435                 :        174 :     const char *p = msg.c_str();
     436                 :        174 :     const char *p_end = p + msg.size();
     437         [ -  + ]:        174 :     if (p != p_end) {
     438                 :            :         unsigned flag_bits;
     439         [ #  # ]:          0 :         if (!unpack_uint_last(&p, p_end, &flag_bits)) {
     440 [ #  # ][ #  # ]:          0 :             throw Xapian::NetworkError("Bad flags in MSG_WRITEACCESS");
                 [ #  # ]
     441                 :            :         }
     442                 :          0 :         flags |= flag_bits &~ Xapian::DB_ACTION_MASK_;
     443                 :            :     }
     444                 :            : 
     445 [ +  - ][ +  - ]:        174 :     wdb = new Xapian::WritableDatabase(context, flags);
     446         [ +  - ]:        174 :     delete db;
     447                 :        174 :     db = wdb;
     448         [ +  - ]:        174 :     msg_update(msg);
     449                 :        174 : }
     450                 :            : 
     451                 :            : void
     452                 :         46 : RemoteServer::msg_reopen(const string & msg)
     453                 :            : {
     454         [ +  + ]:         46 :     if (!db->reopen()) {
     455         [ +  - ]:         36 :         send_message(REPLY_DONE, string());
     456                 :         46 :         return;
     457                 :            :     }
     458                 :         10 :     msg_update(msg);
     459                 :            : }
     460                 :            : 
     461                 :            : void
     462                 :       1186 : RemoteServer::msg_update(const string &)
     463                 :            : {
     464                 :            :     static const char protocol[2] = {
     465                 :            :         char(XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION),
     466                 :            :         char(XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION)
     467                 :            :     };
     468         [ +  - ]:       1186 :     string message(protocol, 2);
     469         [ +  - ]:       1186 :     Xapian::doccount num_docs = db->get_doccount();
     470         [ +  - ]:       1186 :     pack_uint(message, num_docs);
     471 [ +  - ][ +  - ]:       1186 :     pack_uint(message, db->get_lastdocid() - num_docs);
     472         [ +  - ]:       1186 :     Xapian::termcount doclen_lb = db->get_doclength_lower_bound();
     473         [ +  - ]:       1186 :     pack_uint(message, doclen_lb);
     474 [ +  - ][ +  - ]:       1186 :     pack_uint(message, db->get_doclength_upper_bound() - doclen_lb);
     475 [ +  - ][ +  - ]:       1186 :     pack_bool(message, db->has_positions());
     476 [ +  - ][ +  - ]:       1186 :     pack_uint(message, db->get_total_length());
     477 [ +  - ][ +  - ]:       1186 :     message += db->get_uuid();
     478         [ +  - ]:       1186 :     send_message(REPLY_UPDATE, message);
     479                 :       1186 : }
     480                 :            : 
     481                 :            : void
     482                 :      10658 : RemoteServer::msg_query(const string &message_in)
     483                 :            : {
     484                 :      10658 :     const char *p = message_in.c_str();
     485                 :      10658 :     const char *p_end = p + message_in.size();
     486                 :            : 
     487                 :            :     // Unserialise the Query.
     488         [ +  - ]:      10658 :     string serialisation;
     489 [ +  - ][ -  + ]:      10658 :     if (!unpack_string(&p, p_end, serialisation)) {
     490 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_QUERY");
                 [ #  # ]
     491                 :            :     }
     492                 :            : 
     493         [ +  - ]:      21316 :     Xapian::Query query(Xapian::Query::unserialise(serialisation, reg));
     494                 :            : 
     495                 :            :     // Unserialise assorted Enquire settings.
     496                 :            :     Xapian::termcount qlen;
     497                 :            :     Xapian::valueno collapse_max;
     498   [ +  -  -  + ]:      21316 :     if (!unpack_uint(&p, p_end, &qlen) ||
                 [ -  + ]
     499                 :      10658 :         !unpack_uint(&p, p_end, &collapse_max)) {
     500 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_QUERY");
                 [ #  # ]
     501                 :            :     }
     502                 :            : 
     503                 :      10658 :     Xapian::valueno collapse_key = Xapian::BAD_VALUENO;
     504         [ +  + ]:      10658 :     if (collapse_max) {
     505         [ -  + ]:       5092 :         if (!unpack_uint(&p, p_end, &collapse_key)) {
     506 [ #  # ][ #  # ]:          0 :             throw Xapian::NetworkError("Bad MSG_QUERY");
                 [ #  # ]
     507                 :            :         }
     508                 :            :     }
     509                 :            : 
     510 [ +  - ][ -  + ]:      10658 :     if (p_end - p < 4 || static_cast<unsigned char>(*p) > 2) {
     511 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (docid_order)");
                 [ #  # ]
     512                 :            :     }
     513                 :            :     Xapian::Enquire::docid_order order;
     514                 :      10658 :     order = static_cast<Xapian::Enquire::docid_order>(*p++);
     515                 :            : 
     516         [ -  + ]:      10658 :     if (static_cast<unsigned char>(*p) > 3) {
     517 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (sort_by)");
                 [ #  # ]
     518                 :            :     }
     519                 :            :     Xapian::Enquire::Internal::sort_setting sort_by;
     520                 :      10658 :     sort_by = static_cast<Xapian::Enquire::Internal::sort_setting>(*p++);
     521                 :            : 
     522                 :      10658 :     Xapian::valueno sort_key = Xapian::BAD_VALUENO;
     523         [ +  + ]:      10658 :     if (sort_by != Xapian::Enquire::Internal::REL) {
     524         [ -  + ]:        248 :         if (!unpack_uint(&p, p_end, &sort_key)) {
     525 [ #  # ][ #  # ]:          0 :             throw Xapian::NetworkError("Bad MSG_QUERY");
                 [ #  # ]
     526                 :            :         }
     527                 :            :     }
     528                 :            : 
     529                 :            :     bool sort_value_forward;
     530         [ -  + ]:      10658 :     if (!unpack_bool(&p, p_end, &sort_value_forward)) {
     531 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (sort_value_forward)");
                 [ #  # ]
     532                 :            :     }
     533                 :            : 
     534                 :            :     bool full_db_has_positions;
     535         [ -  + ]:      10658 :     if (!unpack_bool(&p, p_end, &full_db_has_positions)) {
     536 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (full_db_has_positions)");
                 [ #  # ]
     537                 :            :     }
     538                 :            : 
     539         [ +  - ]:      10658 :     double time_limit = unserialise_double(&p, p_end);
     540                 :            : 
     541                 :      10658 :     int percent_threshold = *p++;
     542 [ +  - ][ -  + ]:      10658 :     if (percent_threshold < 0 || percent_threshold > 100) {
     543 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (percent_threshold)");
                 [ #  # ]
     544                 :            :     }
     545                 :            : 
     546         [ +  - ]:      10658 :     double weight_threshold = unserialise_double(&p, p_end);
     547         [ -  + ]:      10658 :     if (weight_threshold < 0) {
     548 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("bad message (weight_threshold)");
                 [ #  # ]
     549                 :            :     }
     550                 :            : 
     551                 :            :     // Unserialise the Weight object.
     552         [ +  - ]:      21316 :     string wtname;
     553 [ +  - ][ -  + ]:      10658 :     if (!unpack_string(&p, p_end, wtname)) {
     554 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_QUERY");
                 [ #  # ]
     555                 :            :     }
     556                 :            : 
     557         [ +  - ]:      10658 :     const Xapian::Weight * wttype = reg.get_weighting_scheme(wtname);
     558         [ -  + ]:      10658 :     if (wttype == NULL) {
     559                 :            :         // Note: user weighting schemes should be registered by adding them to
     560                 :            :         // a Registry, and setting the context using
     561                 :            :         // RemoteServer::set_registry().
     562         [ #  # ]:          0 :         throw Xapian::InvalidArgumentError("Weighting scheme " +
     563 [ #  # ][ #  # ]:          0 :                                            wtname + " not registered");
                 [ #  # ]
     564                 :            :     }
     565                 :            : 
     566 [ +  - ][ -  + ]:      10658 :     if (!unpack_string(&p, p_end, serialisation)) {
     567 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_QUERY");
                 [ #  # ]
     568                 :            :     }
     569         [ +  - ]:      21316 :     unique_ptr<Xapian::Weight> wt(wttype->unserialise(serialisation));
     570                 :            : 
     571                 :            :     // Unserialise the RSet object.
     572 [ +  - ][ -  + ]:      10658 :     if (!unpack_string(&p, p_end, serialisation)) {
     573 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_QUERY");
                 [ #  # ]
     574                 :            :     }
     575         [ +  - ]:      21316 :     Xapian::RSet rset = unserialise_rset(serialisation);
     576                 :            : 
     577                 :            :     // Unserialise any MatchSpy objects.
     578                 :      21316 :     vector<Xapian::Internal::opt_intrusive_ptr<Xapian::MatchSpy>> matchspies;
     579         [ +  + ]:      10680 :     while (p != p_end) {
     580         [ +  - ]:         22 :         string spytype;
     581 [ +  - ][ -  + ]:         22 :         if (!unpack_string(&p, p_end, spytype)) {
     582 [ #  # ][ #  # ]:          0 :             throw Xapian::NetworkError("Bad MSG_QUERY");
                 [ #  # ]
     583                 :            :         }
     584         [ +  - ]:         22 :         const Xapian::MatchSpy * spyclass = reg.get_match_spy(spytype);
     585         [ -  + ]:         22 :         if (spyclass == NULL) {
     586 [ #  # ][ #  # ]:          0 :             throw Xapian::InvalidArgumentError("Match spy " + spytype +
     587 [ #  # ][ #  # ]:          0 :                                                " not registered");
     588                 :            :         }
     589                 :            : 
     590 [ +  - ][ -  + ]:         22 :         if (!unpack_string(&p, p_end, serialisation)) {
     591 [ #  # ][ #  # ]:          0 :             throw Xapian::NetworkError("Bad MSG_QUERY");
                 [ #  # ]
     592                 :            :         }
     593                 :            :         matchspies.push_back(spyclass->unserialise(serialisation,
     594 [ +  - ][ +  - ]:         22 :                                                    reg)->release());
     595                 :         22 :     }
     596                 :            : 
     597         [ +  - ]:      21316 :     Xapian::Weight::Internal local_stats;
     598                 :            :     Matcher matcher(*db, full_db_has_positions,
     599         [ +  - ]:      10658 :                     query, qlen, &rset, local_stats, *wt,
     600                 :            :                     false, false,
     601                 :            :                     collapse_key, collapse_max,
     602                 :            :                     percent_threshold, weight_threshold,
     603                 :            :                     order, sort_key, sort_by, sort_value_forward, time_limit,
     604         [ +  - ]:      21316 :                     matchspies);
     605                 :            : 
     606 [ +  - ][ +  - ]:      10658 :     send_message(REPLY_STATS, serialise_stats(local_stats));
     607                 :            : 
     608         [ +  - ]:      21316 :     string message;
     609         [ +  - ]:      10658 :     get_message(active_timeout, message, MSG_GETMSET);
     610                 :      10658 :     p = message.c_str();
     611                 :      10658 :     p_end = p + message.size();
     612                 :            : 
     613                 :            :     Xapian::termcount first;
     614                 :            :     Xapian::termcount maxitems;
     615                 :            :     Xapian::termcount check_at_least;
     616 [ +  - ][ -  + ]:      31974 :     if (!unpack_uint(&p, p_end, &first) ||
     617 [ +  - ][ -  + ]:      21316 :         !unpack_uint(&p, p_end, &maxitems) ||
     618                 :      10658 :         !unpack_uint(&p, p_end, &check_at_least)) {
     619 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_GETMSET");
                 [ #  # ]
     620                 :            :     }
     621                 :            : 
     622         [ +  - ]:      10658 :     message.erase(0, message.size() - (p_end - p));
     623 [ +  - ][ +  - ]:      21316 :     unique_ptr<Xapian::Weight::Internal> total_stats(new Xapian::Weight::Internal);
     624 [ +  - ][ +  - ]:      10658 :     unserialise_stats(message, *total_stats);
     625         [ +  - ]:      10658 :     total_stats->set_bounds_from_db(*db);
     626                 :            : 
     627                 :            :     Xapian::MSet mset = matcher.get_mset(first, maxitems, check_at_least,
     628 [ +  - ][ +  - ]:      10658 :                                          *total_stats, *wt, 0, 0,
     629                 :            :                                          collapse_key, collapse_max,
     630                 :            :                                          percent_threshold, weight_threshold,
     631                 :            :                                          order,
     632                 :            :                                          sort_key, sort_by, sort_value_forward,
     633         [ +  + ]:      21296 :                                          time_limit, matchspies);
     634                 :            :     // FIXME: The local side already has these stats, except for the maxpart
     635                 :            :     // information.
     636                 :      10638 :     mset.internal->set_stats(total_stats.release());
     637                 :            : 
     638         [ +  - ]:      10638 :     message.resize(0);
     639         [ +  + ]:      10660 :     for (auto i : matchspies) {
     640 [ +  - ][ +  - ]:         22 :         pack_string(message, i->serialise_results());
     641                 :         22 :     }
     642 [ +  - ][ +  - ]:      10638 :     message += mset.internal->serialise();
     643         [ +  - ]:      21296 :     send_message(REPLY_RESULTS, message);
     644                 :      10638 : }
     645                 :            : 
     646                 :            : void
     647                 :     235992 : RemoteServer::msg_document(const string &message)
     648                 :            : {
     649                 :     235992 :     const char *p = message.data();
     650                 :     235992 :     const char *p_end = p + message.size();
     651                 :            :     Xapian::docid did;
     652         [ -  + ]:     235992 :     if (!unpack_uint_last(&p, p_end, &did)) {
     653 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_DOCUMENT");
                 [ #  # ]
     654                 :            :     }
     655                 :            : 
     656         [ +  + ]:     235992 :     Xapian::Document doc = db->get_document(did);
     657                 :            : 
     658 [ +  - ][ +  - ]:     229960 :     send_message(REPLY_DOCDATA, doc.get_data());
     659                 :            : 
     660                 :     459920 :     Xapian::ValueIterator i;
     661 [ +  - ][ +  - ]:    1929562 :     for (i = doc.values_begin(); i != doc.values_end(); ++i) {
         [ +  - ][ +  + ]
     662         [ +  - ]:    1699602 :         string item;
     663 [ +  - ][ +  - ]:    1699602 :         pack_uint(item, i.get_valueno());
     664 [ +  - ][ +  - ]:    1699602 :         item += *i;
     665         [ +  - ]:    1699602 :         send_message(REPLY_VALUE, item);
     666                 :    1699602 :     }
     667 [ +  - ][ +  - ]:     459920 :     send_message(REPLY_DONE, string());
     668                 :     229960 : }
     669                 :            : 
     670                 :            : void
     671                 :         20 : RemoteServer::msg_keepalive(const string &)
     672                 :            : {
     673                 :            :     // Ensure *our* database stays alive, as it may contain remote databases!
     674                 :         20 :     db->keep_alive();
     675         [ +  - ]:         20 :     send_message(REPLY_DONE, string());
     676                 :         20 : }
     677                 :            : 
     678                 :            : void
     679                 :        142 : RemoteServer::msg_termexists(const string &term)
     680                 :            : {
     681 [ +  - ][ +  + ]:        142 :     send_message((db->term_exists(term) ? REPLY_TERMEXISTS : REPLY_TERMDOESNTEXIST), string());
                 [ +  - ]
     682                 :        142 : }
     683                 :            : 
     684                 :            : void
     685                 :       2852 : RemoteServer::msg_collfreq(const string &term)
     686                 :            : {
     687         [ +  - ]:       2852 :     string reply;
     688 [ +  - ][ +  - ]:       2852 :     pack_uint_last(reply, db->get_collection_freq(term));
     689         [ +  - ]:       2852 :     send_message(REPLY_COLLFREQ, reply);
     690                 :       2852 : }
     691                 :            : 
     692                 :            : void
     693                 :       1016 : RemoteServer::msg_termfreq(const string &term)
     694                 :            : {
     695         [ +  - ]:       1016 :     string reply;
     696 [ +  - ][ +  - ]:       1016 :     pack_uint_last(reply, db->get_termfreq(term));
     697         [ +  - ]:       1016 :     send_message(REPLY_TERMFREQ, reply);
     698                 :       1016 : }
     699                 :            : 
     700                 :            : void
     701                 :          0 : RemoteServer::msg_freqs(const string &term)
     702                 :            : {
     703         [ #  # ]:          0 :     string msg;
     704 [ #  # ][ #  # ]:          0 :     pack_uint(msg, db->get_termfreq(term));
     705 [ #  # ][ #  # ]:          0 :     pack_uint_last(msg, db->get_collection_freq(term));
     706         [ #  # ]:          0 :     send_message(REPLY_FREQS, msg);
     707                 :          0 : }
     708                 :            : 
     709                 :            : void
     710                 :        110 : RemoteServer::msg_valuestats(const string & message)
     711                 :            : {
     712                 :        110 :     const char *p = message.data();
     713                 :        110 :     const char *p_end = p + message.size();
     714                 :            :     Xapian::valueno slot;
     715         [ -  + ]:        110 :     if (!unpack_uint_last(&p, p_end, &slot)) {
     716 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_VALUESTATS");
                 [ #  # ]
     717                 :            :     }
     718         [ +  - ]:        110 :     string message_out;
     719 [ +  - ][ +  - ]:        110 :     pack_uint(message_out, db->get_value_freq(slot));
     720 [ +  - ][ +  - ]:        110 :     pack_string(message_out, db->get_value_lower_bound(slot));
     721 [ +  - ][ +  - ]:        110 :     message_out += db->get_value_upper_bound(slot);
     722                 :            : 
     723         [ +  - ]:        110 :     send_message(REPLY_VALUESTATS, message_out);
     724                 :        110 : }
     725                 :            : 
     726                 :            : void
     727                 :       6272 : RemoteServer::msg_doclength(const string &message)
     728                 :            : {
     729                 :       6272 :     const char *p = message.data();
     730                 :       6272 :     const char *p_end = p + message.size();
     731                 :            :     Xapian::docid did;
     732         [ -  + ]:       6272 :     if (!unpack_uint_last(&p, p_end, &did)) {
     733 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_DOCLENGTH");
                 [ #  # ]
     734                 :            :     }
     735         [ +  - ]:       6272 :     string reply;
     736 [ +  + ][ +  - ]:       6272 :     pack_uint_last(reply, db->get_doclength(did));
     737         [ +  - ]:       6272 :     send_message(REPLY_DOCLENGTH, reply);
     738                 :        258 : }
     739                 :            : 
     740                 :            : void
     741                 :       6160 : RemoteServer::msg_uniqueterms(const string &message)
     742                 :            : {
     743                 :       6160 :     const char *p = message.data();
     744                 :       6160 :     const char *p_end = p + message.size();
     745                 :            :     Xapian::docid did;
     746         [ -  + ]:       6160 :     if (!unpack_uint_last(&p, p_end, &did)) {
     747 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_UNIQUETERMS");
                 [ #  # ]
     748                 :            :     }
     749         [ +  - ]:       6160 :     string reply;
     750 [ +  + ][ +  - ]:       6160 :     pack_uint_last(reply, db->get_unique_terms(did));
     751         [ +  - ]:       6160 :     send_message(REPLY_UNIQUETERMS, reply);
     752                 :        148 : }
     753                 :            : 
     754                 :            : void
     755                 :       4366 : RemoteServer::msg_commit(const string &)
     756                 :            : {
     757         [ -  + ]:       4366 :     if (!wdb)
     758                 :          0 :         throw_read_only();
     759                 :            : 
     760                 :       4366 :     wdb->commit();
     761                 :            : 
     762         [ +  - ]:       4362 :     send_message(REPLY_DONE, string());
     763                 :       4362 : }
     764                 :            : 
     765                 :            : void
     766                 :         14 : RemoteServer::msg_cancel(const string &)
     767                 :            : {
     768         [ -  + ]:         14 :     if (!wdb)
     769                 :          0 :         throw_read_only();
     770                 :            : 
     771                 :            :     // We can't call cancel since that's an internal method, but this
     772                 :            :     // has the same effect with minimal additional overhead.
     773                 :         14 :     wdb->begin_transaction(false);
     774                 :         14 :     wdb->cancel_transaction();
     775                 :            : 
     776         [ +  - ]:         14 :     send_message(REPLY_DONE, string());
     777                 :         14 : }
     778                 :            : 
     779                 :            : void
     780                 :      37434 : RemoteServer::msg_adddocument(const string & message)
     781                 :            : {
     782         [ -  + ]:      37434 :     if (!wdb)
     783                 :          0 :         throw_read_only();
     784                 :            : 
     785 [ +  - ][ +  + ]:      37434 :     Xapian::docid did = wdb->add_document(unserialise_document(message));
     786                 :            : 
     787         [ +  - ]:      37342 :     string reply;
     788         [ +  - ]:      37342 :     pack_uint_last(reply, did);
     789         [ +  - ]:      37342 :     send_message(REPLY_ADDDOCUMENT, reply);
     790                 :      37342 : }
     791                 :            : 
     792                 :            : void
     793                 :       6052 : RemoteServer::msg_deletedocument(const string & message)
     794                 :            : {
     795         [ -  + ]:       6052 :     if (!wdb)
     796                 :          0 :         throw_read_only();
     797                 :            : 
     798                 :       6052 :     const char *p = message.data();
     799                 :       6052 :     const char *p_end = p + message.size();
     800                 :            :     Xapian::docid did;
     801         [ -  + ]:       6052 :     if (!unpack_uint_last(&p, p_end, &did)) {
     802 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_DELETEDOCUMENT");
                 [ #  # ]
     803                 :            :     }
     804                 :            : 
     805         [ +  + ]:       6052 :     wdb->delete_document(did);
     806                 :            : 
     807 [ +  - ][ +  - ]:       6050 :     send_message(REPLY_DONE, string());
     808                 :       6050 : }
     809                 :            : 
     810                 :            : void
     811                 :          6 : RemoteServer::msg_deletedocumentterm(const string & message)
     812                 :            : {
     813         [ -  + ]:          6 :     if (!wdb)
     814                 :          0 :         throw_read_only();
     815                 :            : 
     816                 :          6 :     wdb->delete_document(message);
     817                 :            : 
     818         [ +  - ]:          6 :     send_message(REPLY_DONE, string());
     819                 :          6 : }
     820                 :            : 
     821                 :            : void
     822                 :       6128 : RemoteServer::msg_replacedocument(const string & message)
     823                 :            : {
     824         [ -  + ]:       6128 :     if (!wdb)
     825                 :          0 :         throw_read_only();
     826                 :            : 
     827                 :       6128 :     const char *p = message.data();
     828                 :       6128 :     const char *p_end = p + message.size();
     829                 :            :     Xapian::docid did;
     830         [ -  + ]:       6128 :     if (!unpack_uint(&p, p_end, &did)) {
     831 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_REPLACEDOCUMENT");
                 [ #  # ]
     832                 :            :     }
     833                 :            : 
     834 [ +  - ][ +  - ]:       6128 :     wdb->replace_document(did, unserialise_document(string(p, p_end)));
                 [ +  + ]
     835                 :            : 
     836 [ +  - ][ +  - ]:       6126 :     send_message(REPLY_DONE, string());
     837                 :       6126 : }
     838                 :            : 
     839                 :            : void
     840                 :         38 : RemoteServer::msg_replacedocumentterm(const string & message)
     841                 :            : {
     842         [ -  + ]:         38 :     if (!wdb)
     843                 :          0 :         throw_read_only();
     844                 :            : 
     845                 :         38 :     const char *p = message.data();
     846                 :         38 :     const char *p_end = p + message.size();
     847         [ +  - ]:         38 :     string unique_term;
     848 [ +  - ][ -  + ]:         38 :     if (!unpack_string(&p, p_end, unique_term)) {
     849 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_REPLACEDOCUMENTTERM");
                 [ #  # ]
     850                 :            :     }
     851 [ +  - ][ +  - ]:         38 :     Xapian::docid did = wdb->replace_document(unique_term, unserialise_document(string(p, p_end)));
                 [ +  - ]
     852                 :            : 
     853         [ +  - ]:         76 :     string reply;
     854         [ +  - ]:         38 :     pack_uint_last(reply, did);
     855         [ +  - ]:         76 :     send_message(REPLY_ADDDOCUMENT, reply);
     856                 :         38 : }
     857                 :            : 
     858                 :            : void
     859                 :         46 : RemoteServer::msg_getmetadata(const string & message)
     860                 :            : {
     861         [ +  - ]:         46 :     send_message(REPLY_METADATA, db->get_metadata(message));
     862                 :         46 : }
     863                 :            : 
     864                 :            : void
     865                 :         16 : RemoteServer::msg_metadatakeylist(const string& message)
     866                 :            : {
     867         [ +  - ]:         16 :     string reply;
     868         [ +  - ]:         32 :     string prev = message;
     869                 :         16 :     const string& prefix = message;
     870         [ +  - ]:        144 :     for (Xapian::TermIterator t = db->metadata_keys_begin(prefix);
           [ +  -  +  + ]
     871                 :         96 :          t != db->metadata_keys_end(prefix);
     872                 :            :          ++t) {
     873         [ -  + ]:         32 :         if (rare(prev.size() > 255))
     874         [ #  # ]:          0 :             prev.resize(255);
     875         [ +  - ]:         32 :         const string& term = *t;
     876         [ +  - ]:         32 :         size_t reuse = common_prefix_length(prev, term);
     877         [ +  - ]:         32 :         reply.append(1, char(reuse));
     878         [ +  - ]:         32 :         pack_uint(reply, term.size() - reuse);
     879         [ +  - ]:         32 :         reply.append(term, reuse, string::npos);
     880         [ +  - ]:         32 :         prev = term;
     881                 :         48 :     }
     882         [ +  - ]:         32 :     send_message(REPLY_METADATAKEYLIST, reply);
     883                 :         16 : }
     884                 :            : 
     885                 :            : void
     886                 :         42 : RemoteServer::msg_setmetadata(const string & message)
     887                 :            : {
     888         [ -  + ]:         42 :     if (!wdb)
     889                 :          0 :         throw_read_only();
     890                 :         42 :     const char *p = message.data();
     891                 :         42 :     const char *p_end = p + message.size();
     892         [ +  - ]:         42 :     string key;
     893 [ +  - ][ -  + ]:         42 :     if (!unpack_string(&p, p_end, key)) {
     894 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_SETMETADATA");
                 [ #  # ]
     895                 :            :     }
     896         [ +  - ]:         84 :     string val(p, p_end - p);
     897         [ +  - ]:         42 :     wdb->set_metadata(key, val);
     898                 :            : 
     899 [ +  - ][ +  - ]:         84 :     send_message(REPLY_DONE, string());
     900                 :         42 : }
     901                 :            : 
     902                 :            : void
     903                 :         10 : RemoteServer::msg_addspelling(const string & message)
     904                 :            : {
     905         [ -  + ]:         10 :     if (!wdb)
     906                 :          0 :         throw_read_only();
     907                 :         10 :     const char *p = message.data();
     908                 :         10 :     const char *p_end = p + message.size();
     909                 :            :     Xapian::termcount freqinc;
     910         [ -  + ]:         10 :     if (!unpack_uint(&p, p_end, &freqinc)) {
     911 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_ADDSPELLING");
                 [ #  # ]
     912                 :            :     }
     913 [ +  - ][ +  - ]:         10 :     wdb->add_spelling(string(p, p_end - p), freqinc);
     914                 :            : 
     915 [ +  - ][ +  - ]:         10 :     send_message(REPLY_DONE, string());
     916                 :         10 : }
     917                 :            : 
     918                 :            : void
     919                 :         16 : RemoteServer::msg_removespelling(const string & message)
     920                 :            : {
     921         [ -  + ]:         16 :     if (!wdb)
     922                 :          0 :         throw_read_only();
     923                 :         16 :     const char *p = message.data();
     924                 :         16 :     const char *p_end = p + message.size();
     925                 :            :     Xapian::termcount freqdec;
     926         [ -  + ]:         16 :     if (!unpack_uint(&p, p_end, &freqdec)) {
     927 [ #  # ][ #  # ]:          0 :         throw Xapian::NetworkError("Bad MSG_REMOVESPELLING");
                 [ #  # ]
     928                 :            :     }
     929         [ +  - ]:         16 :     string reply;
     930 [ +  - ][ +  - ]:         16 :     pack_uint_last(reply, wdb->remove_spelling(string(p, p_end - p), freqdec));
                 [ +  - ]
     931         [ +  - ]:         16 :     send_message(REPLY_REMOVESPELLING, reply);
     932                 :         16 : }

Generated by: LCOV version 1.11