Branch data Line data Source code
1 : : /** @file remote-database.cc
2 : : * @brief Remote backend database class
3 : : */
4 : : /* Copyright (C) 2006,2007,2008,2009,2010 Olly Betts
5 : : * Copyright (C) 2007,2009,2010 Lemur Consulting Ltd
6 : : *
7 : : * This program is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU General Public License as
9 : : * published by the Free Software Foundation; either version 2 of the
10 : : * License, or (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : * GNU General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU General Public License
18 : : * along with this program; if not, write to the Free Software
19 : : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 : : */
21 : :
22 : : #include <config.h>
23 : :
24 : : #include "remote-database.h"
25 : :
26 : : #include "safeerrno.h"
27 : : #include <signal.h>
28 : :
29 : : #include "autoptr.h"
30 : : #include "emptypostlist.h"
31 : : #include "inmemory_positionlist.h"
32 : : #include "net_postlist.h"
33 : : #include "net_termlist.h"
34 : : #include "remote-document.h"
35 : : #include "omassert.h"
36 : : #include "realtime.h"
37 : : #include "serialise.h"
38 : : #include "serialise-double.h"
39 : : #include "str.h"
40 : : #include "stringutils.h" // For STRINGIZE().
41 : : #include "weightinternal.h"
42 : :
43 : : #include <string>
44 : : #include <vector>
45 : :
46 : : #include "xapian/error.h"
47 : : #include "xapian/matchspy.h"
48 : :
49 : : using namespace std;
50 : :
51 : 1554 : RemoteDatabase::RemoteDatabase(int fd, double timeout_,
52 : : const string & context_, bool writable)
53 : : : link(fd, fd, context_),
54 : : context(context_),
55 : : cached_stats_valid(),
56 : : mru_valstats(),
57 : : mru_valno(Xapian::BAD_VALUENO),
58 : 1554 : timeout(timeout_)
59 : : {
60 : : #ifndef __WIN32__
61 : : // It's simplest to just ignore SIGPIPE. We'll still know if the
62 : : // connection dies because we'll get EPIPE back from write().
63 [ # # - + ]: 1554 : if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
64 : 0 : throw Xapian::NetworkError("Couldn't set SIGPIPE to SIG_IGN", errno);
65 : : }
66 : : #endif
67 : :
68 [ # # ][ + + ]: 1554 : if (!writable) {
69 : : // Transactions only make sense when writing, so flag them as
70 : : // "unimplemented" so that our destructor doesn't call dtor_called()
71 : : // since that might try to call commit() which will cause a message to
72 : : // be sent to the remote server and probably an InvalidOperationError
73 : : // exception message to be returned.
74 : 1140 : transaction_state = TRANSACTION_UNIMPLEMENTED;
75 : : }
76 : :
77 : 1554 : string message;
78 : 1554 : char type = get_message(message);
79 : :
80 [ # # # # ]: 1554 : if (reply_type(type) != REPLY_GREETING || message.size() < 3) {
[ # # + - ]
[ - + ][ - + ]
81 [ # # ][ # # ]: 0 : if (type == 'O' && message.size() == size_t('M') && message[0] == ' ') {
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
82 : : // The server reply used to start "OM ", which will now be
83 : : // interpreted as a type 'O' message of length size_t('M')
84 : : // with first character ' '.
85 : 0 : throw Xapian::NetworkError("Server protocol version too old", context);
86 : : }
87 : 0 : throw Xapian::NetworkError("Handshake failed - is this a Xapian server?", context);
88 : : }
89 : :
90 : 1554 : const char *p = message.c_str();
91 : 1554 : const char *p_end = p + message.size();
92 : :
93 : : // The protocol major versions must match. The protocol minor version of
94 : : // the server must be >= that of the client.
95 : 1554 : int protocol_major = static_cast<unsigned char>(*p++);
96 : 1554 : int protocol_minor = static_cast<unsigned char>(*p++);
97 [ # # # # + : 1554 : if (protocol_major != XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION ||
- ][ - + ]
98 : : protocol_minor < XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION) {
99 : 0 : string errmsg("Unknown protocol version ");
100 : 0 : errmsg += str(protocol_major);
101 : 0 : errmsg += '.';
102 : 0 : errmsg += str(protocol_minor);
103 : 0 : errmsg += " ("STRINGIZE(XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION)"."STRINGIZE(XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION)" supported)";
104 : 0 : throw Xapian::NetworkError(errmsg, context);
105 : : }
106 : :
107 : 1554 : doccount = decode_length(&p, p_end, false);
108 : 1554 : lastdocid = decode_length(&p, p_end, false);
109 : 1554 : doclen_lbound = decode_length(&p, p_end, false);
110 : 1554 : doclen_ubound = decode_length(&p, p_end, false);
111 [ # # - + ]: 1554 : if (p == p_end) {
112 : 0 : throw Xapian::NetworkError("Bad greeting message received", context);
113 : : }
114 : 1554 : has_positional_info = (*p++ == '1');
115 : 1554 : total_length = decode_length(&p, p_end, false);
116 : 1554 : uuid.assign(p, p_end);
117 : :
118 [ # # ][ + + ]: 1554 : if (writable) update_stats(MSG_WRITEACCESS);
119 : 1554 : }
120 : :
121 : : RemoteDatabase *
122 : 4446 : RemoteDatabase::as_remotedatabase()
123 : : {
124 : 4446 : return this;
125 : : }
126 : :
127 : : void
128 : 60 : RemoteDatabase::keep_alive()
129 : : {
130 : 60 : send_message(MSG_KEEPALIVE, string());
131 : 60 : string message;
132 : 60 : get_message(message, REPLY_DONE);
133 : 60 : }
134 : :
135 : : TermList *
136 : 48 : RemoteDatabase::open_metadata_keylist(const std::string &prefix) const
137 : : {
138 : : // Ensure that total_length and doccount are up-to-date.
139 [ + + ]: 48 : if (!cached_stats_valid) update_stats();
140 : :
141 : 48 : send_message(MSG_METADATAKEYLIST, prefix);
142 : :
143 : 48 : string message;
144 : : AutoPtr<NetworkTermList> tlist(
145 : : new NetworkTermList(0, doccount,
146 : : Xapian::Internal::RefCntPtr<const RemoteDatabase>(this),
147 : 48 : 0));
148 : 48 : vector<NetworkTermListItem> & items = tlist->items;
149 : :
150 : : char type;
151 [ + + ]: 144 : while ((type = get_message(message)) == REPLY_METADATAKEYLIST) {
152 : 96 : NetworkTermListItem item;
153 : 96 : item.tname = message;
154 : 96 : items.push_back(item);
155 : : }
156 [ - + ]: 48 : if (type != REPLY_DONE) {
157 : 0 : throw Xapian::NetworkError("Bad message received", context);
158 : : }
159 : :
160 : 48 : tlist->current_position = tlist->items.begin();
161 : 48 : return tlist.release();
162 : : }
163 : :
164 : : TermList *
165 : 20382 : RemoteDatabase::open_term_list(Xapian::docid did) const
166 : : {
167 : : Assert(did);
168 : :
169 : : // Ensure that total_length and doccount are up-to-date.
170 [ + + ]: 20382 : if (!cached_stats_valid) update_stats();
171 : :
172 : 20388 : send_message(MSG_TERMLIST, encode_length(did));
173 : :
174 : 20376 : string message;
175 : 20376 : get_message(message, REPLY_DOCLENGTH);
176 : 2322 : const char * p = message.c_str();
177 : 2322 : const char * p_end = p + message.size();
178 : 2322 : Xapian::termcount doclen = decode_length(&p, p_end, false);
179 [ - + ]: 2322 : if (p != p_end) {
180 : 0 : throw Xapian::NetworkError("Bad REPLY_DOCLENGTH message received", context);
181 : : }
182 : :
183 : : AutoPtr<NetworkTermList> tlist(
184 : : new NetworkTermList(doclen, doccount,
185 : : Xapian::Internal::RefCntPtr<const RemoteDatabase>(this),
186 : 2322 : did));
187 : 2322 : vector<NetworkTermListItem> & items = tlist->items;
188 : :
189 : : char type;
190 [ + + ]: 11418 : while ((type = get_message(message)) == REPLY_TERMLIST) {
191 : 9096 : NetworkTermListItem item;
192 : 9096 : p = message.data();
193 : 9096 : p_end = p + message.size();
194 : 9096 : item.wdf = decode_length(&p, p_end, false);
195 : 9096 : item.termfreq = decode_length(&p, p_end, false);
196 : 9096 : item.tname.assign(p, p_end);
197 : 9096 : items.push_back(item);
198 : : }
199 [ - + ]: 2322 : if (type != REPLY_DONE) {
200 : 0 : throw Xapian::NetworkError("Bad message received", context);
201 : : }
202 : :
203 : 2322 : tlist->current_position = tlist->items.begin();
204 : 20376 : return tlist.release();
205 : : }
206 : :
207 : : TermList *
208 : 282 : RemoteDatabase::open_allterms(const string & prefix) const {
209 : : // Ensure that total_length and doccount are up-to-date.
210 [ + + ]: 282 : if (!cached_stats_valid) update_stats();
211 : :
212 : 282 : send_message(MSG_ALLTERMS, prefix);
213 : :
214 : : AutoPtr<NetworkTermList> tlist(
215 : : new NetworkTermList(0, doccount,
216 : : Xapian::Internal::RefCntPtr<const RemoteDatabase>(this),
217 : 282 : 0));
218 : 282 : vector<NetworkTermListItem> & items = tlist->items;
219 : :
220 : 282 : string message;
221 : : char type;
222 [ + + ]: 1110 : while ((type = get_message(message)) == REPLY_ALLTERMS) {
223 : 828 : NetworkTermListItem item;
224 : 828 : const char * p = message.data();
225 : 828 : const char * p_end = p + message.size();
226 : 828 : item.termfreq = decode_length(&p, p_end, false);
227 : 828 : item.tname.assign(p, p_end);
228 : 828 : items.push_back(item);
229 : : }
230 [ - + ]: 276 : if (type != REPLY_DONE) {
231 : 0 : throw Xapian::NetworkError("Bad message received", context);
232 : : }
233 : :
234 : 276 : tlist->current_position = tlist->items.begin();
235 : 288 : return tlist.release();
236 : : }
237 : :
238 : : LeafPostList *
239 : 792 : RemoteDatabase::open_post_list(const string &term) const
240 : : {
241 : 816 : return new NetworkPostList(Xapian::Internal::RefCntPtr<const RemoteDatabase>(this), term);
242 : : }
243 : :
244 : : Xapian::doccount
245 : 792 : RemoteDatabase::read_post_list(const string &term, NetworkPostList & pl) const
246 : : {
247 : 792 : send_message(MSG_POSTLIST, term);
248 : :
249 : 780 : string message;
250 : : char type;
251 : 780 : get_message(message, REPLY_POSTLISTSTART);
252 : :
253 : 780 : const char * p = message.data();
254 : 780 : const char * p_end = p + message.size();
255 : 780 : Xapian::doccount termfreq = decode_length(&p, p_end, false);
256 : :
257 [ + + ]: 20472 : while ((type = get_message(message)) == REPLY_POSTLISTITEM) {
258 : 19692 : pl.append_posting(message);
259 : : }
260 [ - + ]: 780 : if (type != REPLY_DONE) {
261 : 0 : throw Xapian::NetworkError("Bad message received", context);
262 : : }
263 : :
264 : 780 : return termfreq;
265 : : }
266 : :
267 : : PositionList *
268 : 2124 : RemoteDatabase::open_position_list(Xapian::docid did, const string &term) const
269 : : {
270 : 2124 : send_message(MSG_POSITIONLIST, encode_length(did) + term);
271 : :
272 : 2124 : vector<Xapian::termpos> positions;
273 : :
274 : 2124 : string message;
275 : : char type;
276 : 2124 : Xapian::termpos lastpos = static_cast<Xapian::termpos>(-1);
277 [ + + ]: 38682 : while ((type = get_message(message)) == REPLY_POSITIONLIST) {
278 : 36558 : const char * p = message.data();
279 : 36558 : const char * p_end = p + message.size();
280 : 36558 : lastpos += decode_length(&p, p_end, false) + 1;
281 : 36558 : positions.push_back(lastpos);
282 : : }
283 [ - + ]: 2124 : if (type != REPLY_DONE) {
284 : 0 : throw Xapian::NetworkError("Bad message received", context);
285 : : }
286 : :
287 : 2124 : return new InMemoryPositionList(positions);
288 : : }
289 : :
290 : : bool
291 : 30 : RemoteDatabase::has_positions() const
292 : : {
293 [ + + ]: 30 : if (!cached_stats_valid) update_stats();
294 : 24 : return has_positional_info;
295 : : }
296 : :
297 : : void
298 : 146 : RemoteDatabase::reopen()
299 : : {
300 : 146 : update_stats(MSG_REOPEN);
301 : 134 : mru_valno = Xapian::BAD_VALUENO;
302 : 134 : }
303 : :
304 : : void
305 : 24 : RemoteDatabase::close()
306 : : {
307 : 24 : do_close();
308 : 24 : }
309 : :
310 : : // Currently lazy is used when fetching documents from the MSet, and in three
311 : : // cases in multimatch.cc. One of the latter is when using a MatchDecider,
312 : : // which we don't support with the remote backend currently. The others are
313 : : // for the sort key and collapse key which in the remote cases are fetched
314 : : // during the remote match and passed across with the MSet. So we can safely
315 : : // ignore "lazy" here for now without any performance penalty during the match
316 : : // process.
317 : : Xapian::Document::Internal *
318 : 469046 : RemoteDatabase::open_document(Xapian::docid did, bool /*lazy*/) const
319 : : {
320 : : Assert(did);
321 : :
322 : 469058 : send_message(MSG_DOCUMENT, encode_length(did));
323 : 469034 : string doc_data;
324 : 469034 : map<Xapian::valueno, string> values;
325 : 469034 : get_message(doc_data, REPLY_DOCDATA);
326 : :
327 : : reply_type type;
328 : 450950 : string message;
329 [ + + ]: 5304462 : while ((type = get_message(message)) == REPLY_VALUE) {
330 : 4853512 : const char * p = message.data();
331 : 4853512 : const char * p_end = p + message.size();
332 : 4853512 : Xapian::valueno valueno = decode_length(&p, p_end, false);
333 : 4853512 : values.insert(make_pair(valueno, string(p, p_end)));
334 : : }
335 [ - + ]: 450950 : if (type != REPLY_DONE) {
336 : 0 : throw Xapian::NetworkError("Bad message received", context);
337 : : }
338 : :
339 : 487118 : return new RemoteDocument(this, did, doc_data, values);
340 : : }
341 : :
342 : : void
343 : 2204 : RemoteDatabase::update_stats(message_type msg_code) const
344 : : {
345 : 2228 : send_message(msg_code, string());
346 : 2180 : string message;
347 : 2180 : get_message(message, REPLY_UPDATE);
348 : 2180 : const char * p = message.c_str();
349 : 2180 : const char * p_end = p + message.size();
350 : 2180 : doccount = decode_length(&p, p_end, false);
351 : 2180 : lastdocid = decode_length(&p, p_end, false);
352 : 2180 : doclen_lbound = decode_length(&p, p_end, false);
353 : 2180 : doclen_ubound = decode_length(&p, p_end, false);
354 [ - + ]: 2180 : if (p == p_end) {
355 : 0 : throw Xapian::NetworkError("Bad REPLY_UPDATE message received", context);
356 : : }
357 : 2180 : has_positional_info = (*p++ == '1');
358 : 2180 : total_length = decode_length(&p, p_end, false);
359 : 2180 : uuid.assign(p, p_end);
360 : 2180 : cached_stats_valid = true;
361 : 2180 : }
362 : :
363 : : Xapian::doccount
364 : 6532 : RemoteDatabase::get_doccount() const
365 : : {
366 [ + + ]: 6532 : if (!cached_stats_valid) update_stats();
367 : 6526 : return doccount;
368 : : }
369 : :
370 : : Xapian::docid
371 : 2746 : RemoteDatabase::get_lastdocid() const
372 : : {
373 [ + + ]: 2746 : if (!cached_stats_valid) update_stats();
374 : 2746 : return lastdocid;
375 : : }
376 : :
377 : : totlen_t
378 : 0 : RemoteDatabase::get_total_length() const
379 : : {
380 [ # # ]: 0 : if (!cached_stats_valid) update_stats();
381 : 0 : return total_length;
382 : : }
383 : :
384 : : Xapian::doclength
385 : 318 : RemoteDatabase::get_avlength() const
386 : : {
387 [ - + ]: 318 : if (!cached_stats_valid) update_stats();
388 : 318 : return Xapian::doclength(total_length) / doccount;
389 : : }
390 : :
391 : : bool
392 : 396 : RemoteDatabase::term_exists(const string & tname) const
393 : : {
394 : : Assert(!tname.empty());
395 : 396 : send_message(MSG_TERMEXISTS, tname);
396 : 396 : string message;
397 : 396 : reply_type type = get_message(message);
398 [ + + - + ]: 396 : if (type != REPLY_TERMEXISTS && type != REPLY_TERMDOESNTEXIST) {
399 : 0 : throw Xapian::NetworkError("Bad message received", context);
400 : : }
401 : 396 : return (type == REPLY_TERMEXISTS);
402 : : }
403 : :
404 : : Xapian::doccount
405 : 1740 : RemoteDatabase::get_termfreq(const string & tname) const
406 : : {
407 : : Assert(!tname.empty());
408 : 1740 : send_message(MSG_TERMFREQ, tname);
409 : 1740 : string message;
410 : 1740 : get_message(message, REPLY_TERMFREQ);
411 : 1740 : const char * p = message.data();
412 : 1740 : const char * p_end = p + message.size();
413 : 1740 : return decode_length(&p, p_end, false);
414 : : }
415 : :
416 : : Xapian::termcount
417 : 570 : RemoteDatabase::get_collection_freq(const string & tname) const
418 : : {
419 : : Assert(!tname.empty());
420 : 570 : send_message(MSG_COLLFREQ, tname);
421 : 570 : string message;
422 : 570 : get_message(message, REPLY_COLLFREQ);
423 : 570 : const char * p = message.data();
424 : 570 : const char * p_end = p + message.size();
425 : 570 : return decode_length(&p, p_end, false);
426 : : }
427 : :
428 : :
429 : : void
430 : 718 : RemoteDatabase::read_value_stats(Xapian::valueno valno) const
431 : : {
432 [ + + ]: 718 : if (mru_valno != valno) {
433 : 254 : send_message(MSG_VALUESTATS, encode_length(valno));
434 : 254 : string message;
435 : 254 : get_message(message, REPLY_VALUESTATS);
436 : 208 : const char * p = message.data();
437 : 208 : const char * p_end = p + message.size();
438 : 208 : mru_valno = valno;
439 : 208 : mru_valstats.freq = decode_length(&p, p_end, false);
440 : 208 : size_t len = decode_length(&p, p_end, true);
441 : 208 : mru_valstats.lower_bound.assign(p, len);
442 : 208 : p += len;
443 : 208 : len = decode_length(&p, p_end, true);
444 : 208 : mru_valstats.upper_bound.assign(p, len);
445 : 208 : p += len;
446 [ - + ]: 208 : if (p != p_end) {
447 : 0 : throw Xapian::NetworkError("Bad REPLY_VALUESTATS message received", context);
448 : 254 : }
449 : : }
450 : 672 : }
451 : :
452 : : Xapian::doccount
453 : 274 : RemoteDatabase::get_value_freq(Xapian::valueno valno) const
454 : : {
455 : 274 : read_value_stats(valno);
456 : 248 : return mru_valstats.freq;
457 : : }
458 : :
459 : : std::string
460 : 188 : RemoteDatabase::get_value_lower_bound(Xapian::valueno valno) const
461 : : {
462 : 188 : read_value_stats(valno);
463 : 188 : return mru_valstats.lower_bound;
464 : : }
465 : :
466 : : std::string
467 : 256 : RemoteDatabase::get_value_upper_bound(Xapian::valueno valno) const
468 : : {
469 : 256 : read_value_stats(valno);
470 : 236 : return mru_valstats.upper_bound;
471 : : }
472 : :
473 : : Xapian::termcount
474 : 180 : RemoteDatabase::get_doclength_lower_bound() const
475 : : {
476 : 180 : return doclen_lbound;
477 : : }
478 : :
479 : : Xapian::termcount
480 : 132 : RemoteDatabase::get_doclength_upper_bound() const
481 : : {
482 : 132 : return doclen_ubound;
483 : : }
484 : :
485 : : Xapian::termcount
486 : 372 : RemoteDatabase::get_wdf_upper_bound(const string &) const
487 : : {
488 : : // The default implementation returns get_collection_freq(), but we
489 : : // don't want the overhead of a remote message and reply per query
490 : : // term, and we can get called in the middle of a remote exchange
491 : : // too. FIXME: handle this bound in the stats local/remote code...
492 : 372 : return doclen_ubound;
493 : : }
494 : :
495 : : Xapian::termcount
496 : 18726 : RemoteDatabase::get_doclength(Xapian::docid did) const
497 : : {
498 : : Assert(did != 0);
499 : 18738 : send_message(MSG_DOCLENGTH, encode_length(did));
500 : 18714 : string message;
501 : 18714 : get_message(message, REPLY_DOCLENGTH);
502 : 672 : const char * p = message.c_str();
503 : 672 : const char * p_end = p + message.size();
504 : 672 : Xapian::termcount doclen = decode_length(&p, p_end, false);
505 [ - + ]: 672 : if (p != p_end) {
506 : 0 : throw Xapian::NetworkError("Bad REPLY_DOCLENGTH message received", context);
507 : : }
508 : 18714 : return doclen;
509 : : }
510 : :
511 : : reply_type
512 : 5999824 : RemoteDatabase::get_message(string &result, reply_type required_type) const
513 : : {
514 : 5999824 : double end_time = RealTime::end_time(timeout);
515 : 5999824 : reply_type type = static_cast<reply_type>(link.get_message(result, end_time));
516 [ + + ]: 5999824 : if (type == REPLY_EXCEPTION) {
517 : 163578 : unserialise_error(result, "REMOTE:", context);
518 : : }
519 [ + + ][ - + ]: 5945298 : if (required_type != REPLY_MAX && type != required_type) {
520 : 0 : string errmsg("Expecting reply type ");
521 : 0 : errmsg += str(int(required_type));
522 : 0 : errmsg += ", got ";
523 : 0 : errmsg += str(int(type));
524 : 0 : throw Xapian::NetworkError(errmsg);
525 : : }
526 : :
527 : 5945298 : return type;
528 : : }
529 : :
530 : : void
531 : 643116 : RemoteDatabase::send_message(message_type type, const string &message) const
532 : : {
533 : 643116 : double end_time = RealTime::end_time(timeout);
534 : 643116 : link.send_message(static_cast<unsigned char>(type), message, end_time);
535 : 643038 : }
536 : :
537 : : void
538 : 1578 : RemoteDatabase::do_close()
539 : : {
540 : : // In the constructor, we set transaction_state to
541 : : // TRANSACTION_UNIMPLEMENTED if we aren't writable so that we can check
542 : : // it here.
543 : 1578 : bool writable = (transaction_state != TRANSACTION_UNIMPLEMENTED);
544 : :
545 : : // Only call dtor_called() if we're writable.
546 [ + + ]: 1578 : if (writable) dtor_called();
547 : :
548 : : // If we're writable, wait for a confirmation of the close, so we know that
549 : : // changes have been written and flushed, and the database write lock
550 : : // released. For the non-writable case, there's no need to wait, so don't
551 : : // slow down searching by waiting here.
552 : 1578 : link.do_close(writable);
553 : 1578 : }
554 : :
555 : : void
556 : 4422 : RemoteDatabase::set_query(const Xapian::Query::Internal *query,
557 : : Xapian::termcount qlen,
558 : : Xapian::doccount collapse_max,
559 : : Xapian::valueno collapse_key,
560 : : Xapian::Enquire::docid_order order,
561 : : Xapian::valueno sort_key,
562 : : Xapian::Enquire::Internal::sort_setting sort_by,
563 : : bool sort_value_forward,
564 : : int percent_cutoff, Xapian::weight weight_cutoff,
565 : : const Xapian::Weight *wtscheme,
566 : : const Xapian::RSet &omrset,
567 : : const vector<Xapian::MatchSpy *> & matchspies)
568 : : {
569 : 4422 : string tmp = query->serialise();
570 : 4410 : string message = encode_length(tmp.size());
571 : 4410 : message += tmp;
572 : :
573 : : // Serialise assorted Enquire settings.
574 : 4410 : message += encode_length(qlen);
575 : 4410 : message += encode_length(collapse_max);
576 [ + + ]: 4410 : if (collapse_max) message += encode_length(collapse_key);
577 : 4410 : message += char('0' + order);
578 : 4410 : message += encode_length(sort_key);
579 : 4410 : message += char('0' + sort_by);
580 : 4410 : message += char('0' + sort_value_forward);
581 : 4410 : message += char(percent_cutoff);
582 : 4410 : message += serialise_double(weight_cutoff);
583 : :
584 : 4410 : tmp = wtscheme->name();
585 : 4410 : message += encode_length(tmp.size());
586 : 4410 : message += tmp;
587 : :
588 : 4410 : tmp = wtscheme->serialise();
589 : 4410 : message += encode_length(tmp.size());
590 : 4410 : message += tmp;
591 : :
592 : 4410 : tmp = serialise_rset(omrset);
593 : 4410 : message += encode_length(tmp.size());
594 : 4410 : message += tmp;
595 : :
596 : 4410 : vector<Xapian::MatchSpy *>::const_iterator i;
597 [ + + ]: 4476 : for (i = matchspies.begin(); i != matchspies.end(); ++i) {
598 : 66 : tmp = (*i)->name();
599 [ - + ]: 66 : if (tmp.empty()) {
600 : 0 : throw Xapian::UnimplementedError("MatchSpy not suitable for use with remote searches - name() method returned empty string");
601 : : }
602 : 66 : message += encode_length(tmp.size());
603 : 66 : message += tmp;
604 : :
605 : 66 : tmp = (*i)->serialise();
606 : 66 : message += encode_length(tmp.size());
607 : 66 : message += tmp;
608 : : }
609 : :
610 : 4422 : send_message(MSG_QUERY, message);
611 : 4404 : }
612 : :
613 : : bool
614 : 4407 : RemoteDatabase::get_remote_stats(bool nowait, Xapian::Weight::Internal &out)
615 : : {
616 [ + + ][ + + ]: 4407 : if (nowait && !link.ready_to_read()) return false;
[ + + ]
617 : :
618 : 4404 : string message;
619 : 4404 : get_message(message, REPLY_STATS);
620 : 4404 : out = unserialise_stats(message);
621 : :
622 : 4407 : return true;
623 : : }
624 : :
625 : : void
626 : 4404 : RemoteDatabase::send_global_stats(Xapian::doccount first,
627 : : Xapian::doccount maxitems,
628 : : Xapian::doccount check_at_least,
629 : : const Xapian::Weight::Internal &stats)
630 : : {
631 : 4404 : string message = encode_length(first);
632 : 4404 : message += encode_length(maxitems);
633 : 4404 : message += encode_length(check_at_least);
634 : 4404 : message += serialise_stats(stats);
635 : 4404 : send_message(MSG_GETMSET, message);
636 : 4404 : }
637 : :
638 : : void
639 : 4404 : RemoteDatabase::get_mset(Xapian::MSet &mset,
640 : : const vector<Xapian::MatchSpy *> & matchspies)
641 : : {
642 : 4404 : string message;
643 : 4404 : get_message(message, REPLY_RESULTS);
644 : 4404 : const char * p = message.data();
645 : 4404 : const char * p_end = p + message.size();
646 : :
647 : 4404 : vector<Xapian::MatchSpy *>::const_iterator i;
648 [ + + ]: 4470 : for (i = matchspies.begin(); i != matchspies.end(); ++i) {
649 [ - + ]: 66 : if (p == p_end)
650 : 0 : throw Xapian::NetworkError("Expected serialised matchspy");
651 : 66 : size_t len = decode_length(&p, p_end, true);
652 : 66 : string spyresults(p, len);
653 : 66 : p += len;
654 : 66 : (*i)->merge_results(spyresults);
655 : : }
656 : 4404 : mset = unserialise_mset(p, p_end);
657 : 4404 : }
658 : :
659 : : void
660 : 978 : RemoteDatabase::commit()
661 : : {
662 : 984 : send_message(MSG_COMMIT, string());
663 : :
664 : : // We need to wait for a response to ensure documents have been committed.
665 : 972 : string message;
666 : 984 : get_message(message, REPLY_DONE);
667 : 960 : }
668 : :
669 : : void
670 : 28 : RemoteDatabase::cancel()
671 : : {
672 : 28 : cached_stats_valid = false;
673 : 28 : mru_valno = Xapian::BAD_VALUENO;
674 : :
675 : 28 : send_message(MSG_CANCEL, string());
676 : 28 : }
677 : :
678 : : Xapian::docid
679 : 79698 : RemoteDatabase::add_document(const Xapian::Document & doc)
680 : : {
681 : 79698 : cached_stats_valid = false;
682 : 79698 : mru_valno = Xapian::BAD_VALUENO;
683 : :
684 : 79698 : send_message(MSG_ADDDOCUMENT, serialise_document(doc));
685 : :
686 : 79698 : string message;
687 : 79698 : get_message(message, REPLY_ADDDOCUMENT);
688 : :
689 : 79422 : const char * p = message.data();
690 : 79422 : const char * p_end = p + message.size();
691 : 79698 : return decode_length(&p, p_end, false);
692 : : }
693 : :
694 : : void
695 : 18148 : RemoteDatabase::delete_document(Xapian::docid did)
696 : : {
697 : 18148 : cached_stats_valid = false;
698 : 18148 : mru_valno = Xapian::BAD_VALUENO;
699 : :
700 : 18148 : send_message(MSG_DELETEDOCUMENT, encode_length(did));
701 : 18148 : string dummy;
702 : 18154 : get_message(dummy, REPLY_DONE);
703 : 18142 : }
704 : :
705 : : void
706 : 18 : RemoteDatabase::delete_document(const std::string & unique_term)
707 : : {
708 : 18 : cached_stats_valid = false;
709 : 18 : mru_valno = Xapian::BAD_VALUENO;
710 : :
711 : 18 : send_message(MSG_DELETEDOCUMENTTERM, unique_term);
712 : 18 : }
713 : :
714 : : void
715 : 18364 : RemoteDatabase::replace_document(Xapian::docid did,
716 : : const Xapian::Document & doc)
717 : : {
718 : 18364 : cached_stats_valid = false;
719 : 18364 : mru_valno = Xapian::BAD_VALUENO;
720 : :
721 : 18364 : string message = encode_length(did);
722 : 18364 : message += serialise_document(doc);
723 : :
724 : 18364 : send_message(MSG_REPLACEDOCUMENT, message);
725 : 18364 : }
726 : :
727 : : Xapian::docid
728 : 114 : RemoteDatabase::replace_document(const std::string & unique_term,
729 : : const Xapian::Document & doc)
730 : : {
731 : 114 : cached_stats_valid = false;
732 : 114 : mru_valno = Xapian::BAD_VALUENO;
733 : :
734 : 114 : string message = encode_length(unique_term.size());
735 : 114 : message += unique_term;
736 : 114 : message += serialise_document(doc);
737 : :
738 : 114 : send_message(MSG_REPLACEDOCUMENTTERM, message);
739 : :
740 : 114 : get_message(message, REPLY_ADDDOCUMENT);
741 : :
742 : 114 : const char * p = message.data();
743 : 114 : const char * p_end = p + message.size();
744 : 114 : return decode_length(&p, p_end, false);
745 : : }
746 : :
747 : : string
748 : 36 : RemoteDatabase::get_uuid() const
749 : : {
750 : 36 : return uuid;
751 : : }
752 : :
753 : : string
754 : 138 : RemoteDatabase::get_metadata(const string & key) const
755 : : {
756 : 138 : send_message(MSG_GETMETADATA, key);
757 : 138 : string metadata;
758 : 138 : get_message(metadata, REPLY_METADATA);
759 : 0 : return metadata;
760 : : }
761 : :
762 : : void
763 : 114 : RemoteDatabase::set_metadata(const string & key, const string & value)
764 : : {
765 : 114 : string data = encode_length(key.size());
766 : 114 : data += key;
767 : 114 : data += value;
768 : 114 : send_message(MSG_SETMETADATA, data);
769 : 114 : }
770 : :
771 : : void
772 : 30 : RemoteDatabase::add_spelling(const string & word,
773 : : Xapian::termcount freqinc) const
774 : : {
775 : 30 : string data = encode_length(freqinc);
776 : 30 : data += word;
777 : 30 : send_message(MSG_ADDSPELLING, data);
778 : 30 : }
779 : :
780 : : void
781 : 48 : RemoteDatabase::remove_spelling(const string & word,
782 : : Xapian::termcount freqdec) const
783 : : {
784 : 48 : string data = encode_length(freqdec);
785 : 48 : data += word;
786 : 48 : send_message(MSG_REMOVESPELLING, data);
787 : 48 : }
|