xapian-core  1.4.25
remote-database.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2019,2020 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 <signal.h>
27 #include "safesyssocket.h" // For MSG_NOSIGNAL.
28 
29 #include "autoptr.h"
31 #include "net_postlist.h"
32 #include "net_termlist.h"
33 #include "noreturn.h"
34 #include "remote-document.h"
35 #include "omassert.h"
36 #include "realtime.h"
37 #include "net/length.h"
38 #include "net/serialise.h"
39 #include "net/serialise-error.h"
40 #include "serialise-double.h"
41 #include "str.h"
42 #include "stringutils.h" // For STRINGIZE().
43 #include "weight/weightinternal.h"
44 
45 #include <cerrno>
46 #include <string>
47 #include <vector>
48 
49 #include "xapian/constants.h"
50 #include "xapian/error.h"
51 #include "xapian/matchspy.h"
52 
53 using namespace std;
55 
57 static inline bool
58 is_intermediate_reply(int msg_code, int reply_code)
59 {
60  return reply_code == REPLY_ALLTERMS ||
61  reply_code == REPLY_DOCDATA ||
62  reply_code == REPLY_VALUE ||
63  reply_code == REPLY_TERMLIST ||
64  reply_code == REPLY_POSITIONLIST ||
65  reply_code == REPLY_POSTLISTSTART ||
66  reply_code == REPLY_POSTLISTITEM ||
67  reply_code == REPLY_METADATAKEYLIST ||
68  (msg_code == MSG_TERMLIST && reply_code == REPLY_DOCLENGTH);
69 }
70 
71 XAPIAN_NORETURN(static void throw_handshake_failed(const string & context));
72 static void
74 {
75  throw Xapian::NetworkError("Handshake failed - is this a Xapian server?",
76  context);
77 }
78 
79 XAPIAN_NORETURN(static void throw_connection_closed_unexpectedly());
80 static void
82 {
83  throw Xapian::NetworkError("Connection closed unexpectedly");
84 }
85 
86 RemoteDatabase::RemoteDatabase(int fd, double timeout_,
87  const string & context_, bool writable,
88  int flags)
89  : link(fd, fd, context_),
90  context(context_),
92  mru_valstats(),
94  timeout(timeout_)
95 {
96  // On Unix-like platforms we want to avoid generating SIGPIPE when writing
97  // to a socket when the other end has been closed since signals break the
98  // encapsulation of what we're doing inside the library - either user code
99  // would need to handle the SIGPIPE, or we set a signal handler for SIGPIPE
100  // but that would handle *any* SIGPIPE in the process, not just those we
101  // might trigger, and that could break user code which expects to trigger
102  // and handle SIGPIPE.
103  //
104  // We don't need SIGPIPE since we can check errno==EPIPE instead (which is
105  // actually simpler to do).
106 #ifdef SO_NOSIGPIPE
107  // SO_NOSIGPIPE is a non-standardised socket option supported by a number
108  // of platforms - at least DragonFlyBSD, FreeBSD, macOS (not older
109  // versions, e.g. 10.15 apparently lacks it), NetBSD, Solaris; notably not
110  // supported by Linux or OpenBSD though.
111  //
112  // We use it where supported due to one big advantage over POSIX's
113  // MSG_NOSIGNAL which is that we can just set it once for a socket whereas
114  // with MSG_NOSIGNAL we need to call send(..., MSG_NOSIGNAL) instead of
115  // write(...), but send() only works on sockets, so with MSG_NOSIGNAL any
116  // code which might be working with files or pipes as well as sockets needs
117  // conditional handling depending on whether the fd is a socket or not.
118  int on = 1;
119  if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE,
120  reinterpret_cast<char*>(&on), sizeof(on)) < 0) {
121  throw Xapian::NetworkError("Couldn't set SO_NOSIGPIPE on socket",
122  errno);
123  }
124 #elif defined MSG_NOSIGNAL
125  // We can use send(..., MSG_NOSIGNAL) to avoid generating SIGPIPE
126  // (MSG_NOSIGNAL was added in POSIX.1-2008). This seems to be pretty much
127  // universally supported by current Unix-like platforms, but older macOS
128  // and Solaris apparently didn't have it.
129 #elif defined __WIN32__
130  // Sockets apparently don't trigger SIGPIPE here.
131 #else
132  // It's simplest to just ignore SIGPIPE. Not ideal, but it seems only old
133  // versions of macOS and Solaris will end up here so let's not bother
134  // trying to do any clever trickery.
135  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
136  throw Xapian::NetworkError("Couldn't set SIGPIPE to SIG_IGN", errno);
137  }
138 #endif
139 
140  if (!writable) {
141  // Transactions only make sense when writing, so flag them as
142  // "unimplemented" so that our destructor doesn't call dtor_called()
143  // since that might try to call commit() which will cause a message to
144  // be sent to the remote server and probably an InvalidOperationError
145  // exception message to be returned.
147  }
148 
150 
151  if (writable) {
152  if (flags & Xapian::DB_RETRY_LOCK) {
153  const string & body = encode_length(flags & Xapian::DB_RETRY_LOCK);
155  } else {
157  }
158  }
159 }
160 
161 void
163 {
164  send_message(MSG_KEEPALIVE, string());
165  string message;
166  get_message(message, REPLY_DONE);
167 }
168 
169 TermList *
170 RemoteDatabase::open_metadata_keylist(const std::string &prefix) const
171 {
172  // Ensure that total_length and doccount are up-to-date.
174 
176 
177  string message;
178  AutoPtr<NetworkTermList> tlist(
179  new NetworkTermList(0, doccount,
181  0));
182  vector<NetworkTermListItem> & items = tlist->items;
183 
184  string term = prefix;
185  while (get_message_or_done(message, REPLY_METADATAKEYLIST)) {
186  NetworkTermListItem item;
187  term.resize(size_t(static_cast<unsigned char>(message[0])));
188  term.append(message, 1, string::npos);
189  item.tname = term;
190  items.push_back(item);
191  }
192 
193  tlist->current_position = tlist->items.begin();
194  return tlist.release();
195 }
196 
197 TermList *
199 {
200  Assert(did);
201 
202  // Ensure that total_length and doccount are up-to-date.
204 
206 
207  string message;
208  get_message(message, REPLY_DOCLENGTH);
209  const char * p = message.c_str();
210  const char * p_end = p + message.size();
211  Xapian::termcount doclen;
212  decode_length(&p, p_end, doclen);
213  if (p != p_end) {
214  throw Xapian::NetworkError("Bad REPLY_DOCLENGTH message received", context);
215  }
216 
217  AutoPtr<NetworkTermList> tlist(
218  new NetworkTermList(doclen, doccount,
220  did));
221  vector<NetworkTermListItem> & items = tlist->items;
222 
223  string term;
224  while (get_message_or_done(message, REPLY_TERMLIST)) {
225  NetworkTermListItem item;
226  p = message.data();
227  p_end = p + message.size();
228  decode_length(&p, p_end, item.wdf);
229  decode_length(&p, p_end, item.termfreq);
230  term.resize(size_t(static_cast<unsigned char>(*p++)));
231  term.append(p, p_end);
232  item.tname = term;
233  items.push_back(item);
234  }
235 
236  tlist->current_position = tlist->items.begin();
237  return tlist.release();
238 }
239 
240 TermList *
241 RemoteDatabase::open_allterms(const string & prefix) const {
242  // Ensure that total_length and doccount are up-to-date.
244 
245  send_message(MSG_ALLTERMS, prefix);
246 
247  AutoPtr<NetworkTermList> tlist(
248  new NetworkTermList(0, doccount,
250  0));
251  vector<NetworkTermListItem> & items = tlist->items;
252 
253  string term = prefix;
254  string message;
255  while (get_message_or_done(message, REPLY_ALLTERMS)) {
256  NetworkTermListItem item;
257  const char * p = message.data();
258  const char * p_end = p + message.size();
259  decode_length(&p, p_end, item.termfreq);
260  term.resize(size_t(static_cast<unsigned char>(*p++)));
261  term.append(p, p_end);
262  item.tname = term;
263  items.push_back(item);
264  }
265 
266  tlist->current_position = tlist->items.begin();
267  return tlist.release();
268 }
269 
270 LeafPostList *
271 RemoteDatabase::open_post_list(const string &term) const
272 {
274 }
275 
277 RemoteDatabase::read_post_list(const string &term, NetworkPostList & pl) const
278 {
279  send_message(MSG_POSTLIST, term);
280 
281  string message;
283 
284  const char * p = message.data();
285  const char * p_end = p + message.size();
286  Xapian::doccount termfreq;
287  decode_length(&p, p_end, termfreq);
288 
289  while (get_message_or_done(message, REPLY_POSTLISTITEM)) {
290  pl.append_posting(message);
291  }
292 
293  return termfreq;
294 }
295 
296 PositionList *
297 RemoteDatabase::open_position_list(Xapian::docid did, const string &term) const
298 {
300 
301  vector<Xapian::termpos> positions;
302 
303  string message;
304  Xapian::termpos lastpos = static_cast<Xapian::termpos>(-1);
305  while (get_message_or_done(message, REPLY_POSITIONLIST)) {
306  const char * p = message.data();
307  const char * p_end = p + message.size();
308  Xapian::termpos inc;
309  decode_length(&p, p_end, inc);
310  lastpos += inc + 1;
311  positions.push_back(lastpos);
312  }
313 
314  return new InMemoryPositionList(positions);
315 }
316 
317 bool
319 {
321  return has_positional_info;
322 }
323 
324 bool
326 {
328  return update_stats(MSG_REOPEN);
329 }
330 
331 void
333 {
334  do_close();
335 }
336 
337 // Currently lazy is used:
338 //
339 // * To implement API flag Xapian::DOC_ASSUME_VALID which can be specified when
340 // calling method Database::get_document()
341 //
342 // * To read values for backends without streamed values in SlowValueList
343 //
344 // * If you call get_data(), values_begin() or values_count() on a Document
345 // object passed to a KeyMaker, MatchDecider, MatchSpy during the match
346 //
347 // The first is relevant to the remote backend, but doesn't happen during
348 // the match.
349 //
350 // SlowValueList is used with the remote backend, but not to read values
351 // during the match.
352 //
353 // KeyMaker and MatchSpy happens on the server with the remote backend, so
354 // they aren't relevant here.
355 //
356 // So the cases which are relevant to the remote backend don't matter during
357 // the match, and so we can ignore the lazy flag here without affecting matcher
358 // performance.
361 {
362  Assert(did);
363 
365  string doc_data;
366  map<Xapian::valueno, string> values;
367  get_message(doc_data, REPLY_DOCDATA);
368 
369  string message;
370  while (get_message_or_done(message, REPLY_VALUE)) {
371  const char * p = message.data();
372  const char * p_end = p + message.size();
373  Xapian::valueno slot;
374  decode_length(&p, p_end, slot);
375  values.insert(make_pair(slot, string(p, p_end)));
376  }
377 
378  return new RemoteDocument(this, did, doc_data, values);
379 }
380 
381 bool
382 RemoteDatabase::update_stats(message_type msg_code, const string & body) const
383 {
384  // MSG_MAX signals that we're handling the opening greeting, which isn't in
385  // response to an explicit message.
386  if (msg_code != MSG_MAX)
387  send_message(msg_code, body);
388 
389  string message;
390  if (!get_message_or_done(message, REPLY_UPDATE)) {
391  // The database was already open at the latest revision.
392  return false;
393  }
394 
395  if (message.size() < 3) {
397  }
398  const char *p = message.c_str();
399  const char *p_end = p + message.size();
400 
401  // The protocol major versions must match. The protocol minor version of
402  // the server must be >= that of the client.
403  int protocol_major = static_cast<unsigned char>(*p++);
404  int protocol_minor = static_cast<unsigned char>(*p++);
405  if (protocol_major != XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION ||
406  protocol_minor < XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION) {
407  string errmsg("Server supports protocol version");
408  if (protocol_minor) {
409  errmsg += "s ";
410  errmsg += str(protocol_major);
411  errmsg += ".0 to ";
412  } else {
413  errmsg += ' ';
414  }
415  errmsg += str(protocol_major);
416  errmsg += '.';
417  errmsg += str(protocol_minor);
418  errmsg +=
419  " - client is using "
421  "."
423  throw Xapian::NetworkError(errmsg, context);
424  }
425 
426  decode_length(&p, p_end, doccount);
427  decode_length(&p, p_end, lastdocid);
428  lastdocid += doccount;
429  decode_length(&p, p_end, doclen_lbound);
430  decode_length(&p, p_end, doclen_ubound);
432  if (p == p_end) {
433  throw Xapian::NetworkError("Bad stats update message received", context);
434  }
435  has_positional_info = (*p++ == '1');
436  decode_length(&p, p_end, total_length);
437  uuid.assign(p, p_end);
438  cached_stats_valid = true;
439  return true;
440 }
441 
444 {
446  return doccount;
447 }
448 
451 {
453  return lastdocid;
454 }
455 
458 {
460  return total_length;
461 }
462 
463 bool
464 RemoteDatabase::term_exists(const string & tname) const
465 {
466  Assert(!tname.empty());
468  string message;
469  reply_type type = get_message(message,
472  return (type == REPLY_TERMEXISTS);
473 }
474 
475 void
476 RemoteDatabase::get_freqs(const string & term,
477  Xapian::doccount * termfreq_ptr,
478  Xapian::termcount * collfreq_ptr) const
479 {
480  Assert(!term.empty());
481  string message;
482  const char * p;
483  const char * p_end;
484  if (termfreq_ptr) {
485  if (collfreq_ptr) {
486  send_message(MSG_FREQS, term);
487  get_message(message, REPLY_FREQS);
488  } else {
489  send_message(MSG_TERMFREQ, term);
490  get_message(message, REPLY_TERMFREQ);
491  }
492  p = message.data();
493  p_end = p + message.size();
494  decode_length(&p, p_end, *termfreq_ptr);
495  } else if (collfreq_ptr) {
496  send_message(MSG_COLLFREQ, term);
497  get_message(message, REPLY_COLLFREQ);
498  p = message.data();
499  p_end = p + message.size();
500  }
501  if (collfreq_ptr) {
502  decode_length(&p, p_end, *collfreq_ptr);
503  }
504 }
505 
506 void
508 {
509  if (mru_slot != slot) {
511  string message;
512  get_message(message, REPLY_VALUESTATS);
513  const char * p = message.data();
514  const char * p_end = p + message.size();
515  mru_slot = slot;
516  decode_length(&p, p_end, mru_valstats.freq);
517  size_t len;
518  decode_length_and_check(&p, p_end, len);
519  mru_valstats.lower_bound.assign(p, len);
520  p += len;
521  decode_length_and_check(&p, p_end, len);
522  mru_valstats.upper_bound.assign(p, len);
523  p += len;
524  if (p != p_end) {
525  throw Xapian::NetworkError("Bad REPLY_VALUESTATS message received", context);
526  }
527  }
528 }
529 
532 {
533  read_value_stats(slot);
534  return mru_valstats.freq;
535 }
536 
537 std::string
539 {
540  read_value_stats(slot);
541  return mru_valstats.lower_bound;
542 }
543 
544 std::string
546 {
547  read_value_stats(slot);
548  return mru_valstats.upper_bound;
549 }
550 
553 {
554  return doclen_lbound;
555 }
556 
559 {
560  return doclen_ubound;
561 }
562 
565 {
566  // The default implementation returns get_collection_freq(), but we
567  // don't want the overhead of a remote message and reply per query
568  // term, and we can get called in the middle of a remote exchange
569  // too. FIXME: handle this bound in the stats local/remote code...
570  return doclen_ubound;
571 }
572 
575 {
576  Assert(did != 0);
578  string message;
579  get_message(message, REPLY_DOCLENGTH);
580  const char * p = message.c_str();
581  const char * p_end = p + message.size();
582  Xapian::termcount doclen;
583  decode_length(&p, p_end, doclen);
584  if (p != p_end) {
585  throw Xapian::NetworkError("Bad REPLY_DOCLENGTH message received", context);
586  }
587  return doclen;
588 }
589 
592 {
593  Assert(did != 0);
595  string message;
596  get_message(message, REPLY_UNIQUETERMS);
597  const char * p = message.c_str();
598  const char * p_end = p + message.size();
599  Xapian::termcount doclen;
600  decode_length(&p, p_end, doclen);
601  if (p != p_end) {
602  throw Xapian::NetworkError("Bad REPLY_UNIQUETERMS message received", context);
603  }
604  return doclen;
605 }
606 
609  reply_type required_type,
610  reply_type required_type2) const
611 {
613  int type = link.get_message(result, end_time);
614  if (pending_reply >= 0 && !is_intermediate_reply(pending_reply, type)) {
615  pending_reply = -1;
616  }
617  if (type < 0)
619  if (rare(type) >= REPLY_MAX) {
620  if (required_type == REPLY_UPDATE)
622  string errmsg("Invalid reply type ");
623  errmsg += str(type);
624  throw Xapian::NetworkError(errmsg);
625  }
626  if (type == REPLY_EXCEPTION) {
627  unserialise_error(result, "REMOTE:", context);
628  }
629  if (type != required_type && type != required_type2) {
630  string errmsg("Expecting reply type ");
631  errmsg += str(int(required_type));
632  if (required_type2 != required_type) {
633  errmsg += " or ";
634  errmsg += str(int(required_type2));
635  }
636  errmsg += ", got ";
637  errmsg += str(type);
638  throw Xapian::NetworkError(errmsg);
639  }
640 
641  return static_cast<reply_type>(type);
642 }
643 
644 void
645 RemoteDatabase::send_message(message_type type, const string &message) const
646 {
648  while (pending_reply >= 0) {
649  string dummy;
650  int reply_code = link.get_message(dummy, end_time);
651  if (reply_code < 0)
653  if (!is_intermediate_reply(pending_reply, reply_code)) {
654  pending_reply = -1;
655  }
656  }
657  link.send_message(static_cast<unsigned char>(type), message, end_time);
658  if (type == MSG_REMOVESPELLING) {
659  // MSG_REMOVESPELLING is the only message we send which doesn't expect
660  // a reply (except MSG_SHUTDOWN which causes us to exit anyway).
661  pending_reply = -1;
662  } else {
663  pending_reply = int(type);
664  }
665 }
666 
667 void
669 {
670  // In the constructor, we set transaction_state to
671  // TRANSACTION_UNIMPLEMENTED if we aren't writable so that we can check
672  // it here.
673  bool writable = (transaction_state != TRANSACTION_UNIMPLEMENTED);
674 
675  if (writable) {
676  try {
677  if (transaction_active()) {
679  } else {
680  commit();
681  }
682  } catch (...) {
683  try {
684  link.do_close();
685  } catch (...) {
686  }
687  throw;
688  }
689 
690  // If we're writable, send a shutdown message to the server and wait
691  // for it to close its end of the connection so we know that changes
692  // have been written and flushed, and the database write lock released.
693  // For the non-writable case, there's no need to wait - it would just
694  // slow down searching needlessly.
695  link.shutdown();
696  }
697  link.do_close();
698 }
699 
700 void
702  Xapian::termcount qlen,
703  Xapian::doccount collapse_max,
704  Xapian::valueno collapse_key,
706  Xapian::valueno sort_key,
708  bool sort_value_forward,
709  double time_limit,
710  int percent_cutoff, double weight_cutoff,
711  const Xapian::Weight *wtscheme,
712  const Xapian::RSet &omrset,
714 {
715  string tmp = query.serialise();
716  string message = encode_length(tmp.size());
717  message += tmp;
718 
719  // Serialise assorted Enquire settings.
720  message += encode_length(qlen);
721  message += encode_length(collapse_max);
722  if (collapse_max) message += encode_length(collapse_key);
723  message += char('0' + order);
724  message += encode_length(sort_key);
725  message += char('0' + sort_by);
726  message += char('0' + sort_value_forward);
727  message += serialise_double(time_limit);
728  message += char(percent_cutoff);
729  message += serialise_double(weight_cutoff);
730 
731  tmp = wtscheme->name();
732  message += encode_length(tmp.size());
733  message += tmp;
734 
735  tmp = wtscheme->serialise();
736  message += encode_length(tmp.size());
737  message += tmp;
738 
739  tmp = serialise_rset(omrset);
740  message += encode_length(tmp.size());
741  message += tmp;
742 
743  for (auto i : matchspies) {
744  tmp = i->name();
745  if (tmp.empty()) {
746  throw Xapian::UnimplementedError("MatchSpy subclass not suitable for use with remote searches - name() method returned empty string");
747  }
748  message += encode_length(tmp.size());
749  message += tmp;
750 
751  tmp = i->serialise();
752  message += encode_length(tmp.size());
753  message += tmp;
754  }
755 
756  send_message(MSG_QUERY, message);
757 }
758 
759 bool
761 {
762  if (nowait && !link.ready_to_read()) return false;
763 
764  string message;
765  get_message(message, REPLY_STATS);
766  const char* p = message.data();
767  unserialise_stats(p, p + message.size(), out);
768 
769  return true;
770 }
771 
772 void
774  Xapian::doccount maxitems,
775  Xapian::doccount check_at_least,
776  const Xapian::Weight::Internal &stats)
777 {
778  string message = encode_length(first);
779  message += encode_length(maxitems);
780  message += encode_length(check_at_least);
781  message += serialise_stats(stats);
782  send_message(MSG_GETMSET, message);
783 }
784 
785 void
788 {
789  string message;
790  get_message(message, REPLY_RESULTS);
791  const char * p = message.data();
792  const char * p_end = p + message.size();
793 
794  for (auto i : matchspies) {
795  if (p == p_end)
796  throw Xapian::NetworkError("Expected serialised matchspy");
797  size_t len;
798  decode_length_and_check(&p, p_end, len);
799  string spyresults(p, len);
800  p += len;
801  i->merge_results(spyresults);
802  }
803  mset = unserialise_mset(p, p_end);
804 }
805 
806 void
808 {
809  if (!uncommitted_changes) return;
810 
811  send_message(MSG_COMMIT, string());
812 
813  // We need to wait for a response to ensure documents have been committed.
814  string message;
815  get_message(message, REPLY_DONE);
816 
817  uncommitted_changes = false;
818 }
819 
820 void
822 {
823  if (!uncommitted_changes) return;
824 
825  cached_stats_valid = false;
827 
828  send_message(MSG_CANCEL, string());
829  string dummy;
830  get_message(dummy, REPLY_DONE);
831 
832  uncommitted_changes = false;
833 }
834 
837 {
838  cached_stats_valid = false;
840  uncommitted_changes = true;
841 
843 
844  string message;
845  get_message(message, REPLY_ADDDOCUMENT);
846 
847  const char * p = message.data();
848  const char * p_end = p + message.size();
849  Xapian::docid did;
850  decode_length(&p, p_end, did);
851  return did;
852 }
853 
854 void
856 {
857  cached_stats_valid = false;
859  uncommitted_changes = true;
860 
862  string dummy;
863  get_message(dummy, REPLY_DONE);
864 }
865 
866 void
867 RemoteDatabase::delete_document(const std::string & unique_term)
868 {
869  cached_stats_valid = false;
871  uncommitted_changes = true;
872 
873  send_message(MSG_DELETEDOCUMENTTERM, unique_term);
874  string dummy;
875  get_message(dummy, REPLY_DONE);
876 }
877 
878 void
880  const Xapian::Document & doc)
881 {
882  cached_stats_valid = false;
884  uncommitted_changes = true;
885 
886  string message = encode_length(did);
887  message += serialise_document(doc);
888 
890  string dummy;
891  get_message(dummy, REPLY_DONE);
892 }
893 
895 RemoteDatabase::replace_document(const std::string & unique_term,
896  const Xapian::Document & doc)
897 {
898  cached_stats_valid = false;
900  uncommitted_changes = true;
901 
902  string message = encode_length(unique_term.size());
903  message += unique_term;
904  message += serialise_document(doc);
905 
907 
908  get_message(message, REPLY_ADDDOCUMENT);
909 
910  const char * p = message.data();
911  const char * p_end = p + message.size();
912  Xapian::docid did;
913  decode_length(&p, p_end, did);
914  return did;
915 }
916 
917 string
919 {
920  return uuid;
921 }
922 
923 string
924 RemoteDatabase::get_metadata(const string & key) const
925 {
927  string metadata;
928  get_message(metadata, REPLY_METADATA);
929  return metadata;
930 }
931 
932 void
933 RemoteDatabase::set_metadata(const string & key, const string & value)
934 {
935  uncommitted_changes = true;
936 
937  string data = encode_length(key.size());
938  data += key;
939  data += value;
941  string dummy;
942  get_message(dummy, REPLY_DONE);
943 }
944 
945 void
946 RemoteDatabase::add_spelling(const string & word,
947  Xapian::termcount freqinc) const
948 {
949  uncommitted_changes = true;
950 
951  string data = encode_length(freqinc);
952  data += word;
954  string dummy;
955  get_message(dummy, REPLY_DONE);
956 }
957 
958 void
960  Xapian::termcount freqdec) const
961 {
962  uncommitted_changes = true;
963 
964  string data = encode_length(freqdec);
965  data += word;
967 }
968 
969 bool
971 {
972  throw Xapian::UnimplementedError("Database::locked() not implemented for remote backend");
973 }
void close()
Close the database.
Xapian::doccount get_value_freq(Xapian::valueno slot) const
Return the frequency of a given value slot.
void get_freqs(const string &term, Xapian::doccount *termfreq_ptr, Xapian::termcount *collfreq_ptr) const
Returns frequencies for a term.
An item in a NetworkTermList.
Definition: net_termlist.h:38
The Xapian namespace contains public interfaces for the Xapian library.
Definition: compactor.cc:80
PositionList from an InMemory DB or a Document object.
#define Assert(COND)
Definition: omassert.h:122
Define the XAPIAN_NORETURN macro.
LeafPostList * open_post_list(const string &tname) const
Open a posting list.
Xapian::termcount get_doclength(Xapian::docid did) const
Get the length of a given document.
Termlist in a remote db.
TermList * open_metadata_keylist(const std::string &prefix) const
Get remote metadata key list.
bool uncommitted_changes
True if there are (or may be) uncommitted changes.
length encoded as a string
int get_message(std::string &result, double end_time)
Read one message from fdin.
Xapian::doccount doccount
The remote document count, given at open.
unsigned timeout
A timeout value in milliseconds.
Definition: types.h:100
static void throw_handshake_failed(const string &context)
virtual std::string name() const
Return the name of this weighting scheme.
Definition: weight.cc:135
bool transaction_active() const
Definition: database.h:74
Xapian::termcount wdf
The within-document-frequency of the term.
Definition: net_termlist.h:55
void read_value_stats(Xapian::valueno slot) const
Read the value statistics for a value from a remote database.
bool has_positional_info
Has positional information?
XAPIAN_TOTALLENGTH_TYPE totallength
The total length of all documents in a database.
Definition: types.h:139
include <sys/socket.h> with portability workarounds.
Constants in the Xapian namespace.
Xapian::termcount get_doclength_upper_bound() const
Get an upper bound on the length of a document in this DB.
double end_time(double timeout)
Return the end time for a timeout in timeout seconds.
Definition: realtime.h:95
TermList * open_allterms(const string &prefix) const
Iterate all terms.
reply_type
Reply types (server -> client).
string context
The context to return with any error messages.
A document in the database, possibly plus modifications.
Definition: document.h:43
Class representing a list of search results.
Definition: mset.h:44
#define STRINGIZE(X)
The STRINGIZE macro converts its parameter into a string constant.
Definition: stringutils.h:36
RemoteDatabase is the baseclass for remote database implementations.
Abstract base class for termlists.
Definition: termlist.h:39
STL namespace.
Convert types to std::string.
Xapian::termcount get_wdf_upper_bound(const string &term) const
Get an upper bound on the wdf of term term.
bool has_positions() const
Check whether this database contains any positional information.
std::string upper_bound
An upper bound on the values stored in the given value slot.
Definition: valuestats.h:41
Abstract base class for leaf postlists.
Definition: leafpostlist.h:38
Xapian::Document::Internal * open_document(Xapian::docid did, bool lazy) const
Get a remote document.
virtual std::string serialise() const
Return this object&#39;s parameters serialised as a single string.
Definition: weight.cc:141
std::string encode_length(T len)
Encode a length as a variable-length string.
Definition: length.h:36
Xapian::docid get_lastdocid() const
Get the last used docid.
static bool is_intermediate_reply(int msg_code, int reply_code)
Return true if further replies should be expected.
#define rare(COND)
Definition: config.h:565
Xapian::docid add_document(const Xapian::Document &doc)
Add a new document to the database.
void send_message(message_type type, const string &data) const
Send a message to the server.
double timeout
The timeout value used in network communications, in seconds.
void do_close()
Close the socket.
void delete_document(Xapian::docid did)
Delete a document in the database.
int pending_reply
Are we currently expecting a reply?
Xapian::totallength total_length
The total length of all documents in this database.
Xapian::doccount termfreq
The term frequency.
Definition: net_termlist.h:48
Xapian::doccount freq
The number of documents which have a (non-empty) value stored in the slot.
Definition: valuestats.h:33
ValueStats mru_valstats
The most recently used value statistics.
std::string lower_bound
A lower bound on the values stored in the given value slot.
Definition: valuestats.h:37
enum Xapian::Database::Internal::@2 transaction_state
Transaction state.
Xapian::doccount get_doccount() const
Get the document count.
Xapian::MSet unserialise_mset(const char *p, const char *p_end)
Unserialise a serialised Xapian::MSet object.
Definition: serialise.cc:154
Hierarchy of classes which Xapian can throw as exceptions.
void commit()
Commit pending modifications to the database.
Xapian::docid lastdocid
The remote last docid, given at open.
unsigned XAPIAN_TERMCOUNT_BASE_TYPE termcount
A counts of terms.
Definition: types.h:72
void unserialise_error(const string &serialised_error, const string &prefix, const string &new_context)
Unserialise a Xapian::Error object and throw it.
functions to serialise and unserialise a double
string get_metadata(const string &key) const
Get the metadata associated with a given key.
const char * dummy[]
Definition: version_h.cc:7
OwnedRemoteConnection link
The object which does the I/O.
Xapian::termcount doclen_lbound
A lower bound on the smallest document length in this database.
MatchSpy implementation.
Xapian::Weight::Internal class, holding database and term statistics.
void get_mset(Xapian::MSet &mset, const vector< Xapian::Internal::opt_intrusive_ptr< Xapian::MatchSpy >> &matchspies)
Get the MSet from the remote server.
Class to hold statistics for a given collection.
void set_query(const Xapian::Query &query, Xapian::termcount qlen, Xapian::doccount collapse_max, Xapian::valueno collapse_key, Xapian::Enquire::docid_order order, Xapian::valueno sort_key, Xapian::Enquire::Internal::sort_setting sort_by, bool sort_value_forward, double time_limit, int percent_cutoff, double weight_cutoff, const Xapian::Weight *wtscheme, const Xapian::RSet &omrset, const vector< Xapian::Internal::opt_intrusive_ptr< Xapian::MatchSpy >> &matchspies)
Set the query.
string uuid
The UUID of the remote database.
Xapian::valueno mru_slot
The value slot for the most recently used value statistics.
void send_global_stats(Xapian::doccount first, Xapian::doccount maxitems, Xapian::doccount check_at_least, const Xapian::Weight::Internal &stats)
Send the global stats to the remote server.
string str(int value)
Convert int to std::string.
Definition: str.cc:90
A position list in a inmemory database.
bool get_remote_stats(bool nowait, Xapian::Weight::Internal &out)
Get the stats from the remote server.
std::string serialise() const
Serialise this object into a string.
Definition: query.cc:193
bool term_exists(const string &tname) const
Check if term exists.
std::string get_value_lower_bound(Xapian::valueno slot) const
Get a lower bound on the values stored in the given value slot.
Postlists for remote databases.
#define XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION
void keep_alive()
Send a keep-alive message.
string serialise_stats(const Xapian::Weight::Internal &stats)
Serialise a stats object.
Definition: serialise.cc:42
void replace_document(Xapian::docid did, const Xapian::Document &doc)
Replace a given document in the database.
functions to convert classes to strings and back
string serialise_rset(const Xapian::RSet &rset)
Serialise a Xapian::RSet object.
Definition: serialise.cc:217
#define XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION
static Xapian::Query query(Xapian::Query::op op, const string &t1=string(), const string &t2=string(), const string &t3=string(), const string &t4=string(), const string &t5=string(), const string &t6=string(), const string &t7=string(), const string &t8=string(), const string &t9=string(), const string &t10=string())
Definition: api_anydb.cc:63
string serialise_document(const Xapian::Document &doc)
Serialise a Xapian::Document object.
Definition: serialise.cc:251
bool update_stats(message_type msg_code=MSG_UPDATE, const std::string &body=std::string()) const
void unserialise_stats(const char *p, const char *p_end, Xapian::Weight::Internal &stat)
Unserialise a serialised stats object.
Definition: serialise.cc:71
void set_metadata(const string &key, const string &value)
Set the metadata associated with a given key.
std::string serialise_double(double v)
Serialise a double to a string.
void decode_length_and_check(const char **p, const char *end, unsigned &out)
Decode a length encoded by encode_length.
Definition: length.cc:112
unsigned XAPIAN_DOCID_BASE_TYPE doccount
A count of documents.
Definition: types.h:38
Xapian::termcount get_unique_terms(Xapian::docid did) const
Get the number of unique term in document.
A document read from a RemoteDatabase.
void shutdown()
Shutdown the connection.
void remove_spelling(const std::string &, Xapian::termcount freqdec) const
Remove a word from the spelling dictionary.
A term list for a database on the other side of a network connection.
Definition: net_termlist.h:62
PositionList * open_position_list(Xapian::docid did, const string &tname) const
Open a position list for the given term in the given document.
bool locked() const
Return true if the database is open for writing.
Indicates a problem communicating with a remote database.
Definition: error.h:803
void add_spelling(const std::string &, Xapian::termcount) const
Add a word to the spelling dictionary.
Xapian::doccount read_post_list(const string &term, NetworkPostList &pl) const
unsigned valueno
The number for a value slot in a document.
Definition: types.h:108
unsigned XAPIAN_TERMPOS_BASE_TYPE termpos
A term position within a document or query.
Definition: types.h:83
string tname
The "name" of this term.
Definition: net_termlist.h:42
Various handy helpers which std::string really should provide.
std::string get_value_upper_bound(Xapian::valueno slot) const
Get an upper bound on the values stored in the given value slot.
message_type
Message types (client -> server).
std::string get_uuid() const
Get a UUID for the database.
void send_message(char type, const std::string &s, double end_time)
Send a message.
A document read from a RemoteDatabase.
const int DB_RETRY_LOCK
If the database is already locked, retry the lock.
Definition: constants.h:145
Xapian::totallength get_total_length() const
Return the total length of all documents in this database.
static void throw_connection_closed_unexpectedly()
Various assertion macros.
bool reopen()
Reopen the database to the latest available revision.
Functions for handling a time or time interval in a double.
bool ready_to_read() const
See if there is data available to read.
bool get_message_or_done(std::string &message, reply_type required_type) const
unsigned XAPIAN_DOCID_BASE_TYPE docid
A unique identifier for a document.
Definition: types.h:52
void cancel()
Cancel pending modifications to the database.
Class representing a query.
Definition: query.h:46
const valueno BAD_VALUENO
Reserved value to indicate "no valueno".
Definition: types.h:125
Abstract base class for iterating term positions in a document.
Definition: positionlist.h:31
A smart pointer that uses intrusive reference counting.
Definition: intrusive_ptr.h:81
void do_close()
Close the connection.
functions to convert classes to strings and back
reply_type get_message(std::string &message, reply_type required_type, reply_type required_type2) const
Receive a message from the server.
docid_order
Ordering of docids.
Definition: enquire.h:324
void cancel_transaction()
Cancel a transaction.
Definition: database.cc:149
A postlist in a remote database.
Definition: net_postlist.h:36
void decode_length(const char **p, const char *end, unsigned &out)
Decode a length encoded by encode_length.
Definition: length.cc:94
A handle representing a document in a Xapian database.
Definition: document.h:61
Wrapper around standard unique_ptr template.
void append_posting(const string &serialised)
Append a posting to the end of the postlist.
Definition: net_postlist.h:52
A relevance set (R-Set).
Definition: enquire.h:60
UnimplementedError indicates an attempt to use an unimplemented feature.
Definition: error.h:325
Xapian::termcount get_doclength_lower_bound() const
Get a lower bound on the length of a document in this DB.
Abstract base class for weighting schemes.
Definition: weight.h:35
Xapian::termcount doclen_ubound
An upper bound on the greatest document length in this database.
RemoteDatabase(const RemoteDatabase &)
Don&#39;t allow copying.
TermList * open_term_list(Xapian::docid did) const
Get remote termlist.