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 : }
|