LCOV - code coverage report
Current view: top level - net - remoteserver.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core r Lines: 327 367 89.1 %
Date: 2011-08-21 Functions: 34 36 94.4 %
Branches: 67 110 60.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 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/database.h"
      26                 :            : #include "xapian/enquire.h"
      27                 :            : #include "xapian/error.h"
      28                 :            : #include "xapian/matchspy.h"
      29                 :            : #include "xapian/valueiterator.h"
      30                 :            : 
      31                 :            : #include "safeerrno.h"
      32                 :            : #include <signal.h>
      33                 :            : #include <cstdlib>
      34                 :            : 
      35                 :            : #include "autoptr.h"
      36                 :            : #include "multimatch.h"
      37                 :            : #include "omassert.h"
      38                 :            : #include "realtime.h"
      39                 :            : #include "serialise.h"
      40                 :            : #include "serialise-double.h"
      41                 :            : #include "str.h"
      42                 :            : #include "weightinternal.h"
      43                 :            : 
      44                 :            : /// Class to throw when we receive the connection closing message.
      45                 :            : struct ConnectionClosed { };
      46                 :            : 
      47                 :       1554 : RemoteServer::RemoteServer(const std::vector<std::string> &dbpaths,
      48                 :            :                            int fdin_, int fdout_,
      49                 :            :                            double active_timeout_, double idle_timeout_,
      50                 :            :                            bool writable_)
      51                 :            :     : RemoteConnection(fdin_, fdout_, std::string()),
      52                 :            :       db(NULL), wdb(NULL), writable(writable_),
      53                 :       1554 :       active_timeout(active_timeout_), idle_timeout(idle_timeout_)
      54                 :            : {
      55                 :            :     // Catch errors opening the database and propagate them to the client.
      56                 :            :     try {
      57                 :            :         Assert(!dbpaths.empty());
      58                 :            :         // We always open the database read-only to start with.  If we're
      59                 :            :         // writable, the client can ask to be upgraded to write access once
      60                 :            :         // connected if it wants it.
      61                 :       1554 :         db = new Xapian::Database(dbpaths[0]);
      62                 :            :         // Build a better description than Database::get_description() gives
      63                 :            :         // in the variable context.  FIXME: improve Database::get_description()
      64                 :            :         // and then just use that instead.
      65                 :       1554 :         context = dbpaths[0];
      66                 :            : 
      67   [ +  +  #  # ]:       1554 :         if (!writable) {
      68                 :       1140 :             vector<std::string>::const_iterator i(dbpaths.begin());
      69 [ -  + ][ #  # ]:       1140 :             for (++i; i != dbpaths.end(); ++i) {
      70                 :          0 :                 db->add_database(Xapian::Database(*i));
      71                 :          0 :                 context += ' ';
      72                 :          0 :                 context += *i;
      73                 :            :             }
      74                 :            :         } else {
      75                 :            :             AssertEq(dbpaths.size(), 1); // Expecting exactly one database.
      76                 :            :         }
      77                 :          0 :     } catch (const Xapian::Error &err) {
      78                 :            :         // Propagate the exception to the client.
      79                 :          0 :         send_message(REPLY_EXCEPTION, serialise_error(err));
      80                 :            :         // And rethrow it so our caller can log it and close the connection.
      81                 :          0 :         throw;
      82                 :            :     }
      83                 :            : 
      84                 :            : #ifndef __WIN32__
      85                 :            :     // It's simplest to just ignore SIGPIPE.  We'll still know if the
      86                 :            :     // connection dies because we'll get EPIPE back from write().
      87 [ -  + ][ #  # ]:       1554 :     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
      88                 :          0 :         throw Xapian::NetworkError("Couldn't set SIGPIPE to SIG_IGN", errno);
      89                 :            : #endif
      90                 :            : 
      91                 :            :     // Send greeting message.
      92                 :       1554 :     string message;
      93                 :       1554 :     message += char(XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION);
      94                 :       1554 :     message += char(XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION);
      95                 :       1554 :     message += encode_length(db->get_doccount());
      96                 :       1554 :     message += encode_length(db->get_lastdocid());
      97                 :       1554 :     message += encode_length(db->get_doclength_lower_bound());
      98                 :       1554 :     message += encode_length(db->get_doclength_upper_bound());
      99 [ +  + ][ #  # ]:       1554 :     message += (db->has_positions() ? '1' : '0');
     100                 :            :     // FIXME: clumsy to reverse calculate total_len like this:
     101                 :       1554 :     totlen_t total_len = totlen_t(db->get_avlength() * db->get_doccount() + .5);
     102                 :       1554 :     message += encode_length(total_len);
     103                 :            :     //message += encode_length(db->get_total_length());
     104                 :       1554 :     string uuid = db->get_uuid();
     105                 :       1554 :     message += uuid;
     106                 :       1554 :     send_message(REPLY_GREETING, message);
     107                 :       1554 : }
     108                 :            : 
     109                 :       1554 : RemoteServer::~RemoteServer()
     110                 :            : {
     111 [ +  - ][ #  # ]:       1554 :     delete db;
     112                 :            :     // wdb is either NULL or equal to db, so we shouldn't delete it too!
     113                 :       1554 : }
     114                 :            : 
     115                 :            : message_type
     116                 :     644592 : RemoteServer::get_message(double timeout, string & result,
     117                 :            :                           message_type required_type)
     118                 :            : {
     119                 :     644592 :     double end_time = RealTime::end_time(timeout);
     120                 :     644592 :     unsigned int type = RemoteConnection::get_message(result, end_time);
     121                 :            : 
     122                 :            :     // Handle "shutdown connection" message here.
     123         [ +  + ]:     643452 :     if (type == MSG_SHUTDOWN) throw ConnectionClosed();
     124         [ -  + ]:     643038 :     if (type >= MSG_MAX) {
     125                 :          0 :         string errmsg("Invalid message type ");
     126                 :          0 :         errmsg += str(type);
     127                 :          0 :         throw Xapian::NetworkError(errmsg);
     128                 :            :     }
     129 [ +  + ][ -  + ]:     643038 :     if (required_type != MSG_MAX && type != unsigned(required_type)) {
     130                 :          0 :         string errmsg("Expecting message type ");
     131                 :          0 :         errmsg += str(int(required_type));
     132                 :          0 :         errmsg += ", got ";
     133                 :          0 :         errmsg += str(int(type));
     134                 :          0 :         throw Xapian::NetworkError(errmsg);
     135                 :            :     }
     136                 :     643038 :     return static_cast<message_type>(type);
     137                 :            : }
     138                 :            : 
     139                 :            : void
     140                 :    5999824 : RemoteServer::send_message(reply_type type, const string &message)
     141                 :            : {
     142                 :    5999824 :     double end_time = RealTime::end_time(active_timeout);
     143                 :    5999824 :     unsigned char type_as_char = static_cast<unsigned char>(type);
     144                 :    5999824 :     RemoteConnection::send_message(type_as_char, message, end_time);
     145                 :    5999824 : }
     146                 :            : 
     147                 :            : typedef void (RemoteServer::* dispatch_func)(const string &);
     148                 :            : 
     149                 :            : void
     150                 :     640188 : RemoteServer::run()
     151                 :            : {
     152                 :     638634 :     while (true) {
     153                 :            :         try {
     154                 :            :             /* This list needs to be kept in the same order as the list of
     155                 :            :              * message types in "remoteprotocol.h". Note that messages at the
     156                 :            :              * end of the list in "remoteprotocol.h" can be omitted if they
     157                 :            :              * don't correspond to dispatch actions.
     158                 :            :              */
     159                 :            :             static const dispatch_func dispatch[] = {
     160                 :            :                 &RemoteServer::msg_allterms,
     161                 :            :                 &RemoteServer::msg_collfreq,
     162                 :            :                 &RemoteServer::msg_document,
     163                 :            :                 &RemoteServer::msg_termexists,
     164                 :            :                 &RemoteServer::msg_termfreq,
     165                 :            :                 &RemoteServer::msg_valuestats,
     166                 :            :                 &RemoteServer::msg_keepalive,
     167                 :            :                 &RemoteServer::msg_doclength,
     168                 :            :                 &RemoteServer::msg_query,
     169                 :            :                 &RemoteServer::msg_termlist,
     170                 :            :                 &RemoteServer::msg_positionlist,
     171                 :            :                 &RemoteServer::msg_postlist,
     172                 :            :                 &RemoteServer::msg_reopen,
     173                 :            :                 &RemoteServer::msg_update,
     174                 :            :                 &RemoteServer::msg_adddocument,
     175                 :            :                 &RemoteServer::msg_cancel,
     176                 :            :                 &RemoteServer::msg_deletedocumentterm,
     177                 :            :                 &RemoteServer::msg_commit,
     178                 :            :                 &RemoteServer::msg_replacedocument,
     179                 :            :                 &RemoteServer::msg_replacedocumentterm,
     180                 :            :                 &RemoteServer::msg_deletedocument,
     181                 :            :                 &RemoteServer::msg_writeaccess,
     182                 :            :                 &RemoteServer::msg_getmetadata,
     183                 :            :                 &RemoteServer::msg_setmetadata,
     184                 :            :                 &RemoteServer::msg_addspelling,
     185                 :            :                 &RemoteServer::msg_removespelling,
     186                 :            :                 0, // MSG_GETMSET - used during a conversation.
     187                 :            :                 0, // MSG_SHUTDOWN - handled by get_message().
     188                 :            :                 &RemoteServer::msg_openmetadatakeylist,
     189                 :            :             };
     190                 :            : 
     191                 :     640188 :             string message;
     192                 :     640188 :             size_t type = get_message(idle_timeout, message);
     193   [ +  -  -  + ]:     638634 :             if (type >= sizeof(dispatch)/sizeof(dispatch[0]) || !dispatch[type]) {
     194                 :          0 :                 string errmsg("Unexpected message type ");
     195                 :          0 :                 errmsg += str(type);
     196                 :          0 :                 throw Xapian::InvalidArgumentError(errmsg);
     197                 :            :             }
     198         [ -  + ]:     694714 :             (this->*(dispatch[type]))(message);
     199                 :         12 :         } catch (const Xapian::NetworkTimeoutError & e) {
     200                 :            :             try {
     201                 :            :                 // We've had a timeout, so the client may not be listening, so
     202                 :            :                 // set the end_time to 1 and if we can't send the message right
     203                 :            :                 // away, just exit and the client will cope.
     204                 :          6 :                 send_message(REPLY_EXCEPTION, serialise_error(e), 1.0);
     205                 :          0 :             } catch (...) {
     206                 :            :             }
     207                 :            :             // And rethrow it so our caller can log it and close the
     208                 :            :             // connection.
     209                 :          6 :             throw;
     210                 :       2268 :         } catch (const Xapian::NetworkError &) {
     211                 :            :             // All other network errors mean we are fatally confused and are
     212                 :            :             // unlikely to be able to communicate further across this
     213                 :            :             // connection.  So we don't try to propagate the error to the
     214                 :            :             // client, but instead just rethrow the exception so our caller can
     215                 :            :             // log it and close the connection.
     216                 :       1134 :             throw;
     217                 :     109052 :         } catch (const Xapian::Error &e) {
     218                 :            :             // Propagate the exception to the client, then return to the main
     219                 :            :             // message handling loop.
     220                 :      54526 :             send_message(REPLY_EXCEPTION, serialise_error(e));
     221                 :        414 :         } catch (ConnectionClosed &) {
     222                 :            :             return;
     223                 :          0 :         } catch (...) {
     224                 :            :             // Propagate an unknown exception to the client.
     225                 :          0 :             send_message(REPLY_EXCEPTION, string());
     226                 :            :             // And rethrow it so our caller can log it and close the
     227                 :            :             // connection.
     228                 :          0 :             throw;
     229                 :            :         }
     230                 :            :     }
     231                 :            : }
     232                 :            : 
     233                 :            : void
     234                 :        282 : RemoteServer::msg_allterms(const string &message)
     235                 :            : {
     236                 :        282 :     const string & prefix = message;
     237                 :            : 
     238                 :        282 :     const Xapian::TermIterator end = db->allterms_end(prefix);
     239         [ +  + ]:       1110 :     for (Xapian::TermIterator t = db->allterms_begin(prefix); t != end; ++t) {
     240                 :        828 :         string item = encode_length(t.get_termfreq());
     241                 :        828 :         item += *t;
     242                 :        828 :         send_message(REPLY_ALLTERMS, item);
     243                 :        276 :     }
     244                 :            : 
     245                 :        282 :     send_message(REPLY_DONE, string());
     246                 :        276 : }
     247                 :            : 
     248                 :            : void
     249                 :      20376 : RemoteServer::msg_termlist(const string &message)
     250                 :            : {
     251                 :      20376 :     const char *p = message.data();
     252                 :      20376 :     const char *p_end = p + message.size();
     253                 :      20376 :     Xapian::docid did = decode_length(&p, p_end, false);
     254                 :            : 
     255                 :      20376 :     send_message(REPLY_DOCLENGTH, encode_length(db->get_doclength(did)));
     256                 :       2322 :     const Xapian::TermIterator end = db->termlist_end(did);
     257         [ +  + ]:      11418 :     for (Xapian::TermIterator t = db->termlist_begin(did); t != end; ++t) {
     258                 :       9096 :         string item = encode_length(t.get_wdf());
     259                 :       9096 :         item += encode_length(t.get_termfreq());
     260                 :       9096 :         item += *t;
     261                 :       9096 :         send_message(REPLY_TERMLIST, item);
     262                 :       2322 :     }
     263                 :            : 
     264                 :       2322 :     send_message(REPLY_DONE, string());
     265                 :       2322 : }
     266                 :            : 
     267                 :            : void
     268                 :       2124 : RemoteServer::msg_positionlist(const string &message)
     269                 :            : {
     270                 :       2124 :     const char *p = message.data();
     271                 :       2124 :     const char *p_end = p + message.size();
     272                 :       2124 :     Xapian::docid did = decode_length(&p, p_end, false);
     273                 :       2124 :     string term(p, p_end - p);
     274                 :            : 
     275                 :       2124 :     Xapian::termpos lastpos = static_cast<Xapian::termpos>(-1);
     276                 :       2124 :     const Xapian::PositionIterator end = db->positionlist_end(did, term);
     277         [ +  + ]:      38682 :     for (Xapian::PositionIterator i = db->positionlist_begin(did, term);
     278                 :            :          i != end; ++i) {
     279                 :      36558 :         Xapian::termpos pos = *i;
     280                 :      36558 :         send_message(REPLY_POSITIONLIST, encode_length(pos - lastpos - 1));
     281                 :      36558 :         lastpos = pos;
     282                 :       2124 :     }
     283                 :            : 
     284                 :       2124 :     send_message(REPLY_DONE, string());
     285                 :       2124 : }
     286                 :            : 
     287                 :            : void
     288                 :        780 : RemoteServer::msg_postlist(const string &message)
     289                 :            : {
     290                 :        780 :     const string & term = message;
     291                 :            : 
     292                 :        780 :     Xapian::doccount termfreq = db->get_termfreq(term);
     293                 :        780 :     Xapian::termcount collfreq = db->get_collection_freq(term);
     294                 :        780 :     send_message(REPLY_POSTLISTSTART, encode_length(termfreq) + encode_length(collfreq));
     295                 :            : 
     296                 :        780 :     Xapian::docid lastdocid = 0;
     297                 :        780 :     const Xapian::PostingIterator end = db->postlist_end(term);
     298         [ +  + ]:      20472 :     for (Xapian::PostingIterator i = db->postlist_begin(term);
     299                 :            :          i != end; ++i) {
     300                 :            : 
     301                 :      19692 :         Xapian::docid newdocid = *i;
     302                 :      19692 :         string reply = encode_length(newdocid - lastdocid - 1);
     303                 :      19692 :         reply += encode_length(i.get_wdf());
     304                 :            : 
     305                 :      19692 :         send_message(REPLY_POSTLISTITEM, reply);
     306                 :      19692 :         lastdocid = newdocid;
     307                 :        780 :     }
     308                 :            : 
     309                 :        780 :     send_message(REPLY_DONE, string());
     310                 :        780 : }
     311                 :            : 
     312                 :            : void
     313                 :        414 : RemoteServer::msg_writeaccess(const string & msg)
     314                 :            : {
     315         [ -  + ]:        414 :     if (!writable) 
     316                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     317                 :            : 
     318                 :        414 :     wdb = new Xapian::WritableDatabase(context, Xapian::DB_OPEN);
     319         [ +  - ]:        414 :     delete db;
     320                 :        414 :     db = wdb;
     321                 :        414 :     msg_update(msg);
     322                 :        414 : }
     323                 :            : 
     324                 :            : void
     325                 :        134 : RemoteServer::msg_reopen(const string & msg)
     326                 :            : {
     327                 :        134 :     db->reopen();
     328                 :        134 :     msg_update(msg);
     329                 :        134 : }
     330                 :            : 
     331                 :            : void
     332                 :       2180 : RemoteServer::msg_update(const string &)
     333                 :            : {
     334                 :            :     // reopen() doesn't do anything for a WritableDatabase, so there's
     335                 :            :     // no harm in calling it unconditionally.
     336                 :       2180 :     db->reopen();
     337                 :            : 
     338                 :       2180 :     string message = encode_length(db->get_doccount());
     339                 :       2180 :     message += encode_length(db->get_lastdocid());
     340                 :       2180 :     message += encode_length(db->get_doclength_lower_bound());
     341                 :       2180 :     message += encode_length(db->get_doclength_upper_bound());
     342         [ +  + ]:       2180 :     message += (db->has_positions() ? '1' : '0');
     343                 :            :     // FIXME: clumsy to reverse calculate total_len like this:
     344                 :       2180 :     totlen_t total_len = totlen_t(db->get_avlength() * db->get_doccount() + .5);
     345                 :       2180 :     message += encode_length(total_len);
     346                 :            :     //message += encode_length(db->get_total_length());
     347                 :       2180 :     string uuid = db->get_uuid();
     348                 :       2180 :     message += uuid;
     349                 :       2180 :     send_message(REPLY_UPDATE, message);
     350                 :       2180 : }
     351                 :            : 
     352                 :            : /** Structure holding a list of match spies.
     353                 :            :  *
     354                 :            :  *  The main reason for the existence of this structure is to make it easy to
     355                 :            :  *  ensure that the match spies are all deleted after use.
     356                 :            :  */
     357                 :       4404 : struct MatchSpyList {
     358                 :            :     vector<Xapian::MatchSpy *> spies;
     359                 :            : 
     360                 :       4404 :     ~MatchSpyList() {
     361                 :       4404 :         vector<Xapian::MatchSpy *>::const_iterator i;
     362         [ +  + ]:       4470 :         for (i = spies.begin(); i != spies.end(); ++i) {
     363         [ +  - ]:         66 :             delete *i;
     364                 :            :         }
     365                 :       4404 :     }
     366                 :            : };
     367                 :            : 
     368                 :            : void
     369                 :       4404 : RemoteServer::msg_query(const string &message_in)
     370                 :            : {
     371                 :       4404 :     const char *p = message_in.c_str();
     372                 :       4404 :     const char *p_end = p + message_in.size();
     373                 :            :     size_t len;
     374                 :            : 
     375                 :            :     // Unserialise the Query.
     376                 :       4404 :     len = decode_length(&p, p_end, true);
     377                 :       4404 :     AutoPtr<Xapian::Query::Internal> query(Xapian::Query::Internal::unserialise(string(p, len), reg));
     378                 :       4404 :     p += len;
     379                 :            : 
     380                 :            :     // Unserialise assorted Enquire settings.
     381                 :       4404 :     Xapian::termcount qlen = decode_length(&p, p_end, false);
     382                 :            : 
     383                 :       4404 :     Xapian::valueno collapse_max = decode_length(&p, p_end, false);
     384                 :            : 
     385                 :       4404 :     Xapian::valueno collapse_key = Xapian::BAD_VALUENO;
     386         [ +  + ]:       4404 :     if (collapse_max) collapse_key = decode_length(&p, p_end, false);
     387                 :            : 
     388 [ +  - ][ +  - ]:       4404 :     if (p_end - p < 4 || *p < '0' || *p > '2') {
                 [ -  + ]
     389                 :          0 :         throw Xapian::NetworkError("bad message (docid_order)");
     390                 :            :     }
     391                 :            :     Xapian::Enquire::docid_order order;
     392                 :       4404 :     order = static_cast<Xapian::Enquire::docid_order>(*p++ - '0');
     393                 :            : 
     394                 :       4404 :     Xapian::valueno sort_key = decode_length(&p, p_end, false);
     395                 :            : 
     396   [ +  -  -  + ]:       4404 :     if (*p < '0' || *p > '3') {
     397                 :          0 :         throw Xapian::NetworkError("bad message (sort_by)");
     398                 :            :     }
     399                 :            :     Xapian::Enquire::Internal::sort_setting sort_by;
     400                 :       4404 :     sort_by = static_cast<Xapian::Enquire::Internal::sort_setting>(*p++ - '0');
     401                 :            : 
     402 [ +  - ][ -  + ]:       4404 :     if (*p < '0' || *p > '1') {
     403                 :          0 :         throw Xapian::NetworkError("bad message (sort_value_forward)");
     404                 :            :     }
     405                 :       4404 :     bool sort_value_forward(*p++ != '0');
     406                 :            : 
     407                 :       4404 :     int percent_cutoff = *p++;
     408 [ +  - ][ -  + ]:       4404 :     if (percent_cutoff < 0 || percent_cutoff > 100) {
     409                 :          0 :         throw Xapian::NetworkError("bad message (percent_cutoff)");
     410                 :            :     }
     411                 :            : 
     412                 :       4404 :     Xapian::weight weight_cutoff = unserialise_double(&p, p_end);
     413         [ -  + ]:       4404 :     if (weight_cutoff < 0) {
     414                 :          0 :         throw Xapian::NetworkError("bad message (weight_cutoff)");
     415                 :            :     }
     416                 :            : 
     417                 :            :     // Unserialise the Weight object.
     418                 :       4404 :     len = decode_length(&p, p_end, true);
     419                 :       4404 :     string wtname(p, len);
     420                 :       4404 :     p += len;
     421                 :            : 
     422                 :       4404 :     const Xapian::Weight * wttype = reg.get_weighting_scheme(wtname);
     423         [ -  + ]:       4404 :     if (wttype == NULL) {
     424                 :            :         // Note: user weighting schemes should be registered by adding them to
     425                 :            :         // a Registry, and setting the context using
     426                 :            :         // RemoteServer::set_registry().
     427                 :            :         throw Xapian::InvalidArgumentError("Weighting scheme " +
     428                 :          0 :                                            wtname + " not registered");
     429                 :            :     }
     430                 :            : 
     431                 :       4404 :     len = decode_length(&p, p_end, true);
     432                 :       4404 :     AutoPtr<Xapian::Weight> wt(wttype->unserialise(string(p, len)));
     433                 :       4404 :     p += len;
     434                 :            : 
     435                 :            :     // Unserialise the RSet object.
     436                 :       4404 :     len = decode_length(&p, p_end, true);
     437                 :       4404 :     Xapian::RSet rset = unserialise_rset(string(p, len));
     438                 :       4404 :     p += len;
     439                 :            : 
     440                 :            :     // Unserialise any MatchSpy objects.
     441                 :       4404 :     MatchSpyList matchspies;
     442         [ +  + ]:       4470 :     while (p != p_end) {
     443                 :         66 :         len = decode_length(&p, p_end, true);
     444                 :         66 :         string spytype(p, len);
     445                 :         66 :         const Xapian::MatchSpy * spyclass = reg.get_match_spy(spytype);
     446         [ -  + ]:         66 :         if (spyclass == NULL) {
     447                 :            :             throw Xapian::InvalidArgumentError("Match spy " + spytype +
     448                 :          0 :                                                " not registered");
     449                 :            :         }
     450                 :         66 :         p += len;
     451                 :            : 
     452                 :         66 :         len = decode_length(&p, p_end, true);
     453                 :         66 :         matchspies.spies.push_back(spyclass->unserialise(string(p, len), reg));
     454                 :         66 :         p += len;
     455                 :            :     }
     456                 :            : 
     457                 :       4404 :     Xapian::Weight::Internal local_stats;
     458                 :            :     MultiMatch match(*db, query.get(), qlen, &rset, collapse_max, collapse_key,
     459                 :            :                      percent_cutoff, weight_cutoff, order,
     460                 :            :                      sort_key, sort_by, sort_value_forward, NULL,
     461                 :       4404 :                      local_stats, wt.get(), matchspies.spies, false, false);
     462                 :            : 
     463                 :       4404 :     send_message(REPLY_STATS, serialise_stats(local_stats));
     464                 :            : 
     465                 :       4404 :     string message;
     466                 :       4404 :     get_message(active_timeout, message, MSG_GETMSET);
     467                 :       4404 :     p = message.c_str();
     468                 :       4404 :     p_end = p + message.size();
     469                 :            : 
     470                 :       4404 :     Xapian::termcount first = decode_length(&p, p_end, false);
     471                 :       4404 :     Xapian::termcount maxitems = decode_length(&p, p_end, false);
     472                 :            : 
     473                 :       4404 :     Xapian::termcount check_at_least = 0;
     474                 :       4404 :     check_at_least = decode_length(&p, p_end, false);
     475                 :            : 
     476                 :       4404 :     message.erase(0, message.size() - (p_end - p));
     477                 :       4404 :     Xapian::Weight::Internal total_stats(unserialise_stats(message));
     478                 :       4404 :     total_stats.set_bounds_from_db(*db);
     479                 :            : 
     480                 :       4404 :     Xapian::MSet mset;
     481                 :       4404 :     match.get_mset(first, maxitems, check_at_least, mset, total_stats, 0, 0, 0);
     482                 :            : 
     483                 :       4404 :     message.resize(0);
     484                 :       4404 :     vector<Xapian::MatchSpy *>::const_iterator i;
     485         [ +  + ]:       4470 :     for (i = matchspies.spies.begin(); i != matchspies.spies.end(); ++i) {
     486                 :         66 :         string spy_results = (*i)->serialise_results();
     487                 :         66 :         message += encode_length(spy_results.size());
     488                 :         66 :         message += spy_results;
     489                 :            :     }
     490                 :       4404 :     message += serialise_mset(mset);
     491                 :       4404 :     send_message(REPLY_RESULTS, message);
     492                 :       4404 : }
     493                 :            : 
     494                 :            : void
     495                 :     469034 : RemoteServer::msg_document(const string &message)
     496                 :            : {
     497                 :     469034 :     const char *p = message.data();
     498                 :     469034 :     const char *p_end = p + message.size();
     499                 :     469034 :     Xapian::docid did = decode_length(&p, p_end, false);
     500                 :            : 
     501                 :     469034 :     Xapian::Document doc = db->get_document(did);
     502                 :            : 
     503                 :     450950 :     send_message(REPLY_DOCDATA, doc.get_data());
     504                 :            : 
     505                 :     450950 :     Xapian::ValueIterator i;
     506         [ +  + ]:    5304462 :     for (i = doc.values_begin(); i != doc.values_end(); ++i) {
     507                 :    4853512 :         string item = encode_length(i.get_valueno());
     508                 :    4853512 :         item += *i;
     509                 :    4853512 :         send_message(REPLY_VALUE, item);
     510                 :            :     }
     511                 :     450950 :     send_message(REPLY_DONE, string());
     512                 :     450950 : }
     513                 :            : 
     514                 :            : void
     515                 :         60 : RemoteServer::msg_keepalive(const string &)
     516                 :            : {
     517                 :            :     // Ensure *our* database stays alive, as it may contain remote databases!
     518                 :         60 :     db->keep_alive();
     519                 :         60 :     send_message(REPLY_DONE, string());
     520                 :         60 : }
     521                 :            : 
     522                 :            : void
     523                 :        396 : RemoteServer::msg_termexists(const string &term)
     524                 :            : {
     525         [ +  + ]:        396 :     send_message((db->term_exists(term) ? REPLY_TERMEXISTS : REPLY_TERMDOESNTEXIST), string());
     526                 :        396 : }
     527                 :            : 
     528                 :            : void
     529                 :        570 : RemoteServer::msg_collfreq(const string &term)
     530                 :            : {
     531                 :        570 :     send_message(REPLY_COLLFREQ, encode_length(db->get_collection_freq(term)));
     532                 :        570 : }
     533                 :            : 
     534                 :            : void
     535                 :       1740 : RemoteServer::msg_termfreq(const string &term)
     536                 :            : {
     537                 :       1740 :     send_message(REPLY_TERMFREQ, encode_length(db->get_termfreq(term)));
     538                 :       1740 : }
     539                 :            : 
     540                 :            : void
     541                 :        254 : RemoteServer::msg_valuestats(const string & message)
     542                 :            : {
     543                 :        254 :     const char *p = message.data();
     544                 :        254 :     const char *p_end = p + message.size();
     545         [ +  + ]:        508 :     while (p != p_end) {
     546                 :        254 :         Xapian::valueno valno = decode_length(&p, p_end, false);
     547                 :        254 :         string message_out;
     548                 :        254 :         message_out += encode_length(db->get_value_freq(valno));
     549                 :        208 :         string bound = db->get_value_lower_bound(valno);
     550                 :        208 :         message_out += encode_length(bound.size());
     551                 :        208 :         message_out += bound;
     552                 :        208 :         bound = db->get_value_upper_bound(valno);
     553                 :        208 :         message_out += encode_length(bound.size());
     554                 :        208 :         message_out += bound;
     555                 :            : 
     556                 :        208 :         send_message(REPLY_VALUESTATS, message_out);
     557                 :            :     }
     558                 :        208 : }
     559                 :            : 
     560                 :            : void
     561                 :      18714 : RemoteServer::msg_doclength(const string &message)
     562                 :            : {
     563                 :      18714 :     const char *p = message.data();
     564                 :      18714 :     const char *p_end = p + message.size();
     565                 :      18714 :     Xapian::docid did = decode_length(&p, p_end, false);
     566                 :      18714 :     send_message(REPLY_DOCLENGTH, encode_length(db->get_doclength(did)));
     567                 :        672 : }
     568                 :            : 
     569                 :            : void
     570                 :        972 : RemoteServer::msg_commit(const string &)
     571                 :            : {
     572         [ -  + ]:        972 :     if (!wdb)
     573                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     574                 :            : 
     575                 :        972 :     wdb->commit();
     576                 :            : 
     577                 :        960 :     send_message(REPLY_DONE, string());
     578                 :        960 : }
     579                 :            : 
     580                 :            : void
     581                 :         28 : RemoteServer::msg_cancel(const string &)
     582                 :            : {
     583         [ -  + ]:         28 :     if (!wdb)
     584                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     585                 :            : 
     586                 :            :     // We can't call cancel since that's an internal method, but this
     587                 :            :     // has the same effect with minimal additional overhead.
     588                 :         28 :     wdb->begin_transaction(false);
     589                 :         28 :     wdb->cancel_transaction();
     590                 :         28 : }
     591                 :            : 
     592                 :            : void
     593                 :      79698 : RemoteServer::msg_adddocument(const string & message)
     594                 :            : {
     595         [ -  + ]:      79698 :     if (!wdb)
     596                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     597                 :            : 
     598                 :      79974 :     Xapian::docid did = wdb->add_document(unserialise_document(message));
     599                 :            : 
     600                 :      79422 :     send_message(REPLY_ADDDOCUMENT, encode_length(did));
     601                 :      79422 : }
     602                 :            : 
     603                 :            : void
     604                 :      18148 : RemoteServer::msg_deletedocument(const string & message)
     605                 :            : {
     606         [ -  + ]:      18148 :     if (!wdb)
     607                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     608                 :            : 
     609                 :      18148 :     const char *p = message.data();
     610                 :      18148 :     const char *p_end = p + message.size();
     611                 :      18148 :     Xapian::docid did = decode_length(&p, p_end, false);
     612                 :            : 
     613                 :      18148 :     wdb->delete_document(did);
     614                 :            : 
     615                 :      18142 :     send_message(REPLY_DONE, string());
     616                 :      18142 : }
     617                 :            : 
     618                 :            : void
     619                 :         18 : RemoteServer::msg_deletedocumentterm(const string & message)
     620                 :            : {
     621         [ -  + ]:         18 :     if (!wdb)
     622                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     623                 :            : 
     624                 :         18 :     wdb->delete_document(message);
     625                 :         18 : }
     626                 :            : 
     627                 :            : void
     628                 :      18364 : RemoteServer::msg_replacedocument(const string & message)
     629                 :            : {
     630         [ -  + ]:      18364 :     if (!wdb)
     631                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     632                 :            : 
     633                 :      18364 :     const char *p = message.data();
     634                 :      18364 :     const char *p_end = p + message.size();
     635                 :      18364 :     Xapian::docid did = decode_length(&p, p_end, false);
     636                 :            : 
     637                 :      18364 :     wdb->replace_document(did, unserialise_document(string(p, p_end)));
     638                 :      18364 : }
     639                 :            : 
     640                 :            : void
     641                 :        114 : RemoteServer::msg_replacedocumentterm(const string & message)
     642                 :            : {
     643         [ -  + ]:        114 :     if (!wdb)
     644                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     645                 :            : 
     646                 :        114 :     const char *p = message.data();
     647                 :        114 :     const char *p_end = p + message.size();
     648                 :        114 :     size_t len = decode_length(&p, p_end, true);
     649                 :        114 :     string unique_term(p, len);
     650                 :        114 :     p += len;
     651                 :            : 
     652                 :        114 :     Xapian::docid did = wdb->replace_document(unique_term, unserialise_document(string(p, p_end)));
     653                 :            : 
     654                 :        114 :     send_message(REPLY_ADDDOCUMENT, encode_length(did));
     655                 :        114 : }
     656                 :            : 
     657                 :            : void
     658                 :        138 : RemoteServer::msg_getmetadata(const string & message)
     659                 :            : {
     660                 :        138 :     send_message(REPLY_METADATA, db->get_metadata(message));
     661                 :        138 : }
     662                 :            : 
     663                 :            : void
     664                 :         48 : RemoteServer::msg_openmetadatakeylist(const string & message)
     665                 :            : {
     666                 :         48 :     const Xapian::TermIterator end = db->metadata_keys_end(message);
     667                 :         48 :     Xapian::TermIterator t = db->metadata_keys_begin(message);
     668         [ +  + ]:        144 :     for (; t != end; ++t) {
     669                 :         96 :         send_message(REPLY_METADATAKEYLIST, *t);
     670                 :            :     }
     671                 :            : 
     672                 :         48 :     send_message(REPLY_DONE, string());
     673                 :         48 : }
     674                 :            : 
     675                 :            : void
     676                 :        114 : RemoteServer::msg_setmetadata(const string & message)
     677                 :            : {
     678         [ -  + ]:        114 :     if (!wdb)
     679                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     680                 :        114 :     const char *p = message.data();
     681                 :        114 :     const char *p_end = p + message.size();
     682                 :        114 :     size_t keylen = decode_length(&p, p_end, false);
     683                 :        114 :     string key(p, keylen);
     684                 :        114 :     p += keylen;
     685                 :        114 :     string val(p, p_end - p);
     686                 :        114 :     wdb->set_metadata(key, val);
     687                 :        114 : }
     688                 :            : 
     689                 :            : void
     690                 :         30 : RemoteServer::msg_addspelling(const string & message)
     691                 :            : {
     692         [ -  + ]:         30 :     if (!wdb)
     693                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     694                 :         30 :     const char *p = message.data();
     695                 :         30 :     const char *p_end = p + message.size();
     696                 :         30 :     Xapian::termcount freqinc = decode_length(&p, p_end, false);
     697                 :         30 :     wdb->add_spelling(string(p, p_end - p), freqinc);
     698                 :         30 : }
     699                 :            : 
     700                 :            : void
     701                 :         48 : RemoteServer::msg_removespelling(const string & message)
     702                 :            : {
     703         [ -  + ]:         48 :     if (!wdb)
     704                 :          0 :         throw Xapian::InvalidOperationError("Server is read-only");
     705                 :         48 :     const char *p = message.data();
     706                 :         48 :     const char *p_end = p + message.size();
     707                 :         48 :     Xapian::termcount freqdec = decode_length(&p, p_end, false);
     708                 :         48 :     wdb->remove_spelling(string(p, p_end - p), freqdec);
     709                 :         48 : }

Generated by: LCOV version 1.8