2 * @brief Remote backend database class
4 /* Copyright (C) 2006-2024 Olly Betts
5 * Copyright (C) 2007,2009,2010 Lemur Consulting Ltd
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.
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.
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
24 #include "remote-database.h"
27 #include "safesyssocket.h" // For MSG_NOSIGNAL.
29 #include "api/msetinternal.h"
30 #include "api/smallvector.h"
31 #include "backends/contiguousalldocspostlist.h"
32 #include "backends/inmemory/inmemory_positionlist.h"
33 #include "net_postlist.h"
34 #include "remote-document.h"
37 #include "net/serialise.h"
38 #include "net/serialise-error.h"
40 #include "remote_alltermslist.h"
41 #include "remote_keylist.h"
42 #include "remote_termlist.h"
43 #include "serialise-double.h"
45 #include "stringutils.h" // For STRINGIZE().
46 #include "weight/weightinternal.h"
51 #include <string_view>
54 #include "xapian/constants.h"
55 #include "xapian/error.h"
56 #include "xapian/matchspy.h"
59 using Xapian::Internal::intrusive_ptr
;
61 /// Return true if further replies should be expected.
63 is_intermediate_reply(int reply_code
)
65 return reply_code
== REPLY_DOCDATA
||
66 reply_code
== REPLY_VALUE
||
67 reply_code
== REPLY_TERMLISTHEADER
||
68 reply_code
== REPLY_POSTLISTHEADER
;
73 throw_invalid_operation(const char* message
)
75 throw Xapian::InvalidOperationError(message
);
80 throw_handshake_failed(const string
& context
)
82 throw Xapian::NetworkError("Handshake failed - is this a Xapian server?",
88 throw_connection_closed_unexpectedly()
90 throw Xapian::NetworkError("Connection closed unexpectedly");
93 RemoteDatabase::RemoteDatabase(pair
<int, string
> fd_and_context
,
97 : Xapian::Database::Internal(writable
?
99 TRANSACTION_READONLY
),
100 link(fd_and_context
.first
, fd_and_context
.first
, fd_and_context
.second
),
101 cached_stats_valid(),
103 mru_slot(Xapian::BAD_VALUENO
),
106 update_stats(MSG_MAX
);
109 if (flags
& Xapian::DB_RETRY_LOCK
) {
111 pack_uint_last(message
, unsigned(flags
& Xapian::DB_RETRY_LOCK
));
112 update_stats(MSG_WRITEACCESS
, message
);
114 update_stats(MSG_WRITEACCESS
);
120 RemoteDatabase::positionlist_count(Xapian::docid did
,
121 std::string_view term
) const
123 if (cached_stats_valid
&& !has_positional_info
)
127 pack_uint(message
, did
);
129 send_message(MSG_POSITIONLISTCOUNT
, message
);
131 get_message(message
, REPLY_POSITIONLISTCOUNT
);
132 const char * p
= message
.data();
133 const char * p_end
= p
+ message
.size();
134 Xapian::termcount count
;
135 if (!unpack_uint_last(&p
, p_end
, &count
)) {
136 throw Xapian::NetworkError("Bad REPLY_POSITIONLISTCOUNT",
143 RemoteDatabase::keep_alive()
145 send_message(MSG_KEEPALIVE
, {});
147 get_message(message
, REPLY_DONE
);
151 RemoteDatabase::open_metadata_keylist(std::string_view prefix
) const
153 send_message(MSG_METADATAKEYLIST
, prefix
);
155 get_message(message
, REPLY_METADATAKEYLIST
);
156 return new RemoteKeyList(prefix
, std::move(message
));
160 RemoteDatabase::open_term_list(Xapian::docid did
) const
164 // Ensure that total_length and doccount are up-to-date.
165 if (!cached_stats_valid
) update_stats();
168 pack_uint_last(message
, did
);
169 send_message(MSG_TERMLIST
, message
);
171 get_message(message
, REPLY_TERMLISTHEADER
);
172 const char * p
= message
.c_str();
173 const char * p_end
= p
+ message
.size();
174 Xapian::termcount doclen
;
175 Xapian::termcount num_entries
;
176 if (!unpack_uint(&p
, p_end
, &doclen
) ||
177 !unpack_uint_last(&p
, p_end
, &num_entries
)) {
178 throw Xapian::NetworkError("Bad REPLY_TERMLISTHEADER",
181 get_message(message
, REPLY_TERMLIST
);
182 return new RemoteTermList(num_entries
, doclen
, doccount
, this, did
,
187 RemoteDatabase::open_term_list_direct(Xapian::docid did
) const
189 return RemoteDatabase::open_term_list(did
);
193 RemoteDatabase::open_allterms(string_view prefix
) const
195 send_message(MSG_ALLTERMS
, prefix
);
197 get_message(message
, REPLY_ALLTERMS
);
198 return new RemoteAllTermsList(prefix
, std::move(message
));
202 RemoteDatabase::open_post_list(string_view term
) const
205 if (!cached_stats_valid
) update_stats();
206 if (rare(doccount
== 0))
208 if (doccount
== lastdocid
) {
209 // The used docid range is exactly 1 to doccount inclusive.
210 return new ContiguousAllDocsPostList(doccount
);
214 send_message(MSG_POSTLIST
, term
);
217 get_message(message
, REPLY_POSTLISTHEADER
);
219 const char * p
= message
.data();
220 const char * p_end
= p
+ message
.size();
221 Xapian::doccount termfreq
;
222 if (!unpack_uint_last(&p
, p_end
, &termfreq
)) {
223 unpack_throw_serialisation_error(p
);
226 get_message(message
, REPLY_POSTLIST
);
228 return new NetworkPostList(intrusive_ptr
<const RemoteDatabase
>(this),
235 RemoteDatabase::open_leaf_post_list(string_view
, bool) const
237 // This method is only called during the match, and remote shards are
238 // handled by running the match on the remote.
244 RemoteDatabase::open_position_list(Xapian::docid did
, string_view term
) const
247 pack_uint(message
, did
);
249 send_message(MSG_POSITIONLIST
, message
);
251 get_message(message
, REPLY_POSITIONLIST
);
255 Xapian::VecCOW
<Xapian::termpos
> positions
;
256 Xapian::termpos lastpos
= static_cast<Xapian::termpos
>(-1);
257 const char* p
= message
.data();
258 const char* p_end
= p
+ message
.size();
261 if (!unpack_uint(&p
, p_end
, &inc
)) {
262 unpack_throw_serialisation_error(p
);
264 UNSIGNED_OVERFLOW_OK(lastpos
+= inc
+ 1);
265 positions
.push_back(lastpos
);
268 return new InMemoryPositionList(std::move(positions
));
272 RemoteDatabase::has_positions() const
274 if (!cached_stats_valid
) update_stats();
275 return has_positional_info
;
279 RemoteDatabase::reopen()
281 mru_slot
= Xapian::BAD_VALUENO
;
282 return update_stats(MSG_REOPEN
);
286 RemoteDatabase::close()
291 // Currently lazy is used:
293 // * To implement API flag Xapian::DOC_ASSUME_VALID which can be specified when
294 // calling method Database::get_document()
296 // * To read values for backends without streamed values in SlowValueList
298 // * If you call get_data(), values_begin() or values_count() on a Document
299 // object passed to a KeyMaker, MatchDecider, MatchSpy during the match
301 // The first is relevant to the remote backend, but doesn't happen during
304 // SlowValueList is used with the remote backend, but not to read values
307 // KeyMaker and MatchSpy happens on the server with the remote backend, so
308 // they aren't relevant here.
310 // So the cases which are relevant to the remote backend don't matter during
311 // the match, and so we can ignore the lazy flag here without affecting matcher
313 Xapian::Document::Internal
*
314 RemoteDatabase::open_document(Xapian::docid did
, bool /*lazy*/) const
319 pack_uint_last(message
, did
);
320 send_message(MSG_DOCUMENT
, message
);
323 get_message(doc_data
, REPLY_DOCDATA
);
325 map
<Xapian::valueno
, string
> values
;
326 while (get_message_or_done(message
, REPLY_VALUE
)) {
327 const char * p
= message
.data();
328 const char * p_end
= p
+ message
.size();
329 Xapian::valueno slot
;
330 if (!unpack_uint(&p
, p_end
, &slot
)) {
331 unpack_throw_serialisation_error(p
);
333 values
.insert(make_pair(slot
, string(p
, p_end
)));
336 return new RemoteDocument(this, did
, std::move(doc_data
),
341 RemoteDatabase::update_stats(message_type msg_code
, const string
& body
) const
343 // MSG_MAX signals that we're handling the opening greeting, which isn't in
344 // response to an explicit message.
345 if (msg_code
!= MSG_MAX
)
346 send_message(msg_code
, body
);
349 if (!get_message_or_done(message
, REPLY_UPDATE
)) {
350 // The database was already open at the latest revision.
354 if (message
.size() < 3) {
355 throw_handshake_failed(link
.get_context());
357 const char *p
= message
.c_str();
358 const char *p_end
= p
+ message
.size();
360 // The protocol major versions must match. The protocol minor version of
361 // the server must be >= that of the client.
362 int protocol_major
= static_cast<unsigned char>(*p
++);
363 int protocol_minor
= static_cast<unsigned char>(*p
++);
364 if (protocol_major
!= XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION
||
365 protocol_minor
< XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION
) {
366 string
errmsg("Server supports protocol version");
367 if (protocol_minor
) {
369 errmsg
+= str(protocol_major
);
374 errmsg
+= str(protocol_major
);
376 errmsg
+= str(protocol_minor
);
378 " - client is using "
379 STRINGIZE(XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION
)
381 STRINGIZE(XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION
);
382 throw Xapian::NetworkError(errmsg
, link
.get_context());
385 if (!unpack_uint(&p
, p_end
, &doccount
) ||
386 !unpack_uint(&p
, p_end
, &lastdocid
) ||
387 !unpack_uint(&p
, p_end
, &doclen_lbound
) ||
388 !unpack_uint(&p
, p_end
, &doclen_ubound
) ||
389 !unpack_bool(&p
, p_end
, &has_positional_info
) ||
390 !unpack_uint(&p
, p_end
, &total_length
)) {
391 throw Xapian::NetworkError("Bad stats update message received",
394 lastdocid
+= doccount
;
395 doclen_ubound
+= doclen_lbound
;
396 uuid
.assign(p
, p_end
);
397 cached_stats_valid
= true;
402 RemoteDatabase::get_doccount() const
404 if (!cached_stats_valid
) update_stats();
409 RemoteDatabase::get_lastdocid() const
411 if (!cached_stats_valid
) update_stats();
416 RemoteDatabase::get_total_length() const
418 if (!cached_stats_valid
) update_stats();
423 RemoteDatabase::term_exists(string_view term
) const
426 return get_doccount() != 0;
428 send_message(MSG_TERMEXISTS
, term
);
430 reply_type type
= get_message(message
,
432 REPLY_TERMDOESNTEXIST
);
433 return (type
== REPLY_TERMEXISTS
);
437 RemoteDatabase::get_freqs(string_view term
,
438 Xapian::doccount
* termfreq_ptr
,
439 Xapian::termcount
* collfreq_ptr
) const
441 Assert(!term
.empty());
443 if (termfreq_ptr
&& collfreq_ptr
) {
444 send_message(MSG_FREQS
, term
);
445 get_message(message
, REPLY_FREQS
);
446 const char* p
= message
.data();
447 const char* p_end
= p
+ message
.size();
448 if (unpack_uint(&p
, p_end
, termfreq_ptr
) &&
449 unpack_uint_last(&p
, p_end
, collfreq_ptr
)) {
452 } else if (termfreq_ptr
) {
453 send_message(MSG_TERMFREQ
, term
);
454 get_message(message
, REPLY_TERMFREQ
);
455 const char* p
= message
.data();
456 const char* p_end
= p
+ message
.size();
457 if (unpack_uint_last(&p
, p_end
, termfreq_ptr
)) {
460 } else if (collfreq_ptr
) {
461 send_message(MSG_COLLFREQ
, term
);
462 get_message(message
, REPLY_COLLFREQ
);
463 const char* p
= message
.data();
464 const char* p_end
= p
+ message
.size();
465 if (unpack_uint_last(&p
, p_end
, collfreq_ptr
)) {
472 throw Xapian::NetworkError("Bad REPLY_FREQS/REPLY_TERMFREQ/REPLY_COLLFREQ",
477 RemoteDatabase::read_value_stats(Xapian::valueno slot
) const
479 if (mru_slot
== slot
)
483 pack_uint_last(message
, slot
);
484 send_message(MSG_VALUESTATS
, message
);
486 get_message(message
, REPLY_VALUESTATS
);
487 const char* p
= message
.data();
488 const char* p_end
= p
+ message
.size();
490 if (!unpack_uint(&p
, p_end
, &mru_valstats
.freq
) ||
491 !unpack_string(&p
, p_end
, mru_valstats
.lower_bound
)) {
492 throw Xapian::NetworkError("Bad REPLY_VALUESTATS", link
.get_context());
494 mru_valstats
.upper_bound
.assign(p
, p_end
);
498 RemoteDatabase::get_value_freq(Xapian::valueno slot
) const
500 read_value_stats(slot
);
501 return mru_valstats
.freq
;
505 RemoteDatabase::get_value_lower_bound(Xapian::valueno slot
) const
507 read_value_stats(slot
);
508 return mru_valstats
.lower_bound
;
512 RemoteDatabase::get_value_upper_bound(Xapian::valueno slot
) const
514 read_value_stats(slot
);
515 return mru_valstats
.upper_bound
;
519 RemoteDatabase::get_doclength_lower_bound() const
521 return doclen_lbound
;
525 RemoteDatabase::get_doclength_upper_bound() const
527 return doclen_ubound
;
531 RemoteDatabase::get_wdf_upper_bound(string_view
) const
533 // The default implementation returns get_collection_freq(), but we
534 // don't want the overhead of a remote message and reply per query
535 // term, and we can get called in the middle of a remote exchange
536 // too. FIXME: handle this bound in the stats local/remote code...
537 return doclen_ubound
;
541 RemoteDatabase::get_doclength(Xapian::docid did
) const
545 pack_uint_last(message
, did
);
546 send_message(MSG_DOCLENGTH
, message
);
548 get_message(message
, REPLY_DOCLENGTH
);
549 const char* p
= message
.c_str();
550 const char* p_end
= p
+ message
.size();
551 Xapian::termcount doclen
;
552 if (!unpack_uint_last(&p
, p_end
, &doclen
)) {
553 throw Xapian::NetworkError("Bad REPLY_DOCLENGTH", link
.get_context());
559 RemoteDatabase::get_unique_terms(Xapian::docid did
) const
563 pack_uint_last(message
, did
);
564 send_message(MSG_UNIQUETERMS
, message
);
566 get_message(message
, REPLY_UNIQUETERMS
);
567 const char* p
= message
.c_str();
568 const char* p_end
= p
+ message
.size();
569 Xapian::termcount doclen
;
570 if (!unpack_uint_last(&p
, p_end
, &doclen
)) {
571 throw Xapian::NetworkError("Bad REPLY_DOCLENGTH", link
.get_context());
577 RemoteDatabase::get_wdfdocmax(Xapian::docid did
) const
581 pack_uint_last(message
, did
);
582 send_message(MSG_WDFDOCMAX
, message
);
584 get_message(message
, REPLY_WDFDOCMAX
);
585 const char* p
= message
.c_str();
586 const char* p_end
= p
+ message
.size();
587 Xapian::termcount wdfdocmax
;
588 if (!unpack_uint_last(&p
, p_end
, &wdfdocmax
)) {
589 throw Xapian::NetworkError("Bad REPLY_WDFDOCMAX", link
.get_context());
595 RemoteDatabase::get_message(string
&result
,
596 reply_type required_type
,
597 reply_type required_type2
) const
599 double end_time
= RealTime::end_time(timeout
);
600 int type
= link
.get_message(result
, end_time
);
601 if (pending_reply
&& !is_intermediate_reply(type
)) {
602 pending_reply
= false;
605 throw_connection_closed_unexpectedly();
606 if (rare(type
) >= REPLY_MAX
) {
607 if (required_type
== REPLY_UPDATE
)
608 throw_handshake_failed(link
.get_context());
609 string
errmsg("Invalid reply type ");
611 throw Xapian::NetworkError(errmsg
);
613 if (type
== REPLY_EXCEPTION
) {
614 unserialise_error(result
, "REMOTE:", link
.get_context());
616 if (type
!= required_type
&& type
!= required_type2
) {
617 string
errmsg("Expecting reply type ");
618 errmsg
+= str(int(required_type
));
619 if (required_type2
!= required_type
) {
621 errmsg
+= str(int(required_type2
));
625 throw Xapian::NetworkError(errmsg
);
628 return static_cast<reply_type
>(type
);
632 RemoteDatabase::send_message(message_type type
, string_view message
) const
634 double end_time
= RealTime::end_time(timeout
);
635 while (pending_reply
) {
637 int reply_code
= link
.get_message(dummy
, end_time
);
639 throw_connection_closed_unexpectedly();
640 if (!is_intermediate_reply(reply_code
)) {
641 pending_reply
= false;
644 link
.send_message(static_cast<unsigned char>(type
), message
, end_time
);
645 pending_reply
= true;
649 RemoteDatabase::do_close()
651 if (!is_read_only()) {
653 if (transaction_active()) {
654 end_transaction(false);
666 // If we're writable, send a shutdown message to the server and wait
667 // for it to close its end of the connection so we know that changes
668 // have been written and flushed, and the database write lock released.
669 // For the non-writable case, there's no need to wait - it would just
670 // slow down searching needlessly.
677 RemoteDatabase::set_query(const Xapian::Query
& query
,
678 Xapian::termcount qlen
,
679 Xapian::valueno collapse_key
,
680 Xapian::doccount collapse_max
,
681 Xapian::Enquire::docid_order order
,
682 Xapian::valueno sort_key
,
683 Xapian::Enquire::Internal::sort_setting sort_by
,
684 bool sort_value_forward
,
686 int percent_threshold
, double weight_threshold
,
687 const Xapian::Weight
& wtscheme
,
688 const Xapian::RSet
&omrset
,
689 const vector
<opt_ptr_spy
>& matchspies
) const
692 pack_string(message
, query
.serialise());
694 // Serialise assorted Enquire settings.
695 pack_uint(message
, qlen
);
696 pack_uint(message
, collapse_max
);
697 if (collapse_max
) pack_uint(message
, collapse_key
);
698 message
+= char(order
);
699 message
+= char(sort_by
);
700 if (sort_by
!= Xapian::Enquire::Internal::REL
) {
701 pack_uint(message
, sort_key
);
703 pack_bool(message
, sort_value_forward
);
704 message
+= serialise_double(time_limit
);
705 message
+= char(percent_threshold
);
706 message
+= serialise_double(weight_threshold
);
708 pack_string(message
, wtscheme
.name());
710 pack_string(message
, wtscheme
.serialise());
712 pack_string(message
, serialise_rset(omrset
));
714 for (auto i
: matchspies
) {
715 const string
& name
= i
->name();
717 throw Xapian::UnimplementedError("MatchSpy subclass not suitable for use with remote searches - name() method returned empty string");
719 pack_string(message
, name
);
720 pack_string(message
, i
->serialise());
723 send_message(MSG_QUERY
, message
);
727 RemoteDatabase::accumulate_remote_stats(Xapian::Weight::Internal
& total
) const
730 get_message(message
, REPLY_STATS
);
731 const char* p
= message
.data();
732 Xapian::Weight::Internal remote_stats
;
733 unserialise_stats(p
, p
+ message
.size(), remote_stats
);
734 total
+= remote_stats
;
738 RemoteDatabase::send_global_stats(Xapian::doccount first
,
739 Xapian::doccount maxitems
,
740 Xapian::doccount check_at_least
,
741 const Xapian::KeyMaker
* sorter
,
742 const Xapian::Weight::Internal
&stats
) const
745 pack_uint(message
, first
);
746 pack_uint(message
, maxitems
);
747 pack_uint(message
, check_at_least
);
749 pack_string_empty(message
);
751 const string
& name
= sorter
->name();
753 throw_invalid_operation("sorter reported empty name");
755 pack_string(message
, name
);
756 pack_string(message
, sorter
->serialise());
758 message
+= serialise_stats(stats
);
759 send_message(MSG_GETMSET
, message
);
763 RemoteDatabase::get_mset(const vector
<opt_ptr_spy
>& matchspies
) const
766 get_message(message
, REPLY_RESULTS
);
767 const char * p
= message
.data();
768 const char * p_end
= p
+ message
.size();
771 for (auto i
: matchspies
) {
772 if (!unpack_string(&p
, p_end
, spyresults
)) {
773 throw Xapian::NetworkError("Expected serialised matchspy");
775 i
->merge_results(spyresults
);
778 mset
.internal
->unserialise(p
, p_end
);
783 RemoteDatabase::commit()
785 if (!uncommitted_changes
) return;
787 send_message(MSG_COMMIT
, {});
789 // We need to wait for a response to ensure documents have been committed.
791 get_message(message
, REPLY_DONE
);
793 uncommitted_changes
= false;
797 RemoteDatabase::cancel()
799 if (!uncommitted_changes
) return;
801 cached_stats_valid
= false;
802 mru_slot
= Xapian::BAD_VALUENO
;
804 send_message(MSG_CANCEL
, {});
806 get_message(dummy
, REPLY_DONE
);
808 uncommitted_changes
= false;
812 RemoteDatabase::add_document(const Xapian::Document
& doc
)
814 cached_stats_valid
= false;
815 mru_slot
= Xapian::BAD_VALUENO
;
816 uncommitted_changes
= true;
818 send_message(MSG_ADDDOCUMENT
, serialise_document(doc
));
821 get_message(message
, REPLY_ADDDOCUMENT
);
823 const char* p
= message
.data();
824 const char* p_end
= p
+ message
.size();
826 if (!unpack_uint_last(&p
, p_end
, &did
)) {
827 unpack_throw_serialisation_error(p
);
833 RemoteDatabase::delete_document(Xapian::docid did
)
835 cached_stats_valid
= false;
836 mru_slot
= Xapian::BAD_VALUENO
;
837 uncommitted_changes
= true;
840 pack_uint_last(message
, did
);
841 send_message(MSG_DELETEDOCUMENT
, message
);
843 get_message(message
, REPLY_DONE
);
847 RemoteDatabase::delete_document(std::string_view unique_term
)
849 cached_stats_valid
= false;
850 mru_slot
= Xapian::BAD_VALUENO
;
851 uncommitted_changes
= true;
853 send_message(MSG_DELETEDOCUMENTTERM
, unique_term
);
855 get_message(dummy
, REPLY_DONE
);
859 RemoteDatabase::replace_document(Xapian::docid did
,
860 const Xapian::Document
& doc
)
862 cached_stats_valid
= false;
863 mru_slot
= Xapian::BAD_VALUENO
;
864 uncommitted_changes
= true;
867 pack_uint(message
, did
);
868 message
+= serialise_document(doc
);
870 send_message(MSG_REPLACEDOCUMENT
, message
);
872 get_message(message
, REPLY_DONE
);
876 RemoteDatabase::replace_document(std::string_view unique_term
,
877 const Xapian::Document
& doc
)
879 cached_stats_valid
= false;
880 mru_slot
= Xapian::BAD_VALUENO
;
881 uncommitted_changes
= true;
884 pack_string(message
, unique_term
);
885 message
+= serialise_document(doc
);
887 send_message(MSG_REPLACEDOCUMENTTERM
, message
);
889 get_message(message
, REPLY_ADDDOCUMENT
);
891 const char* p
= message
.data();
892 const char* p_end
= p
+ message
.size();
894 if (!unpack_uint_last(&p
, p_end
, &did
)) {
895 unpack_throw_serialisation_error(p
);
901 RemoteDatabase::get_uuid() const
907 RemoteDatabase::get_metadata(string_view key
) const
909 send_message(MSG_GETMETADATA
, key
);
911 get_message(metadata
, REPLY_METADATA
);
916 RemoteDatabase::set_metadata(string_view key
, string_view value
)
918 uncommitted_changes
= true;
921 pack_string(message
, key
);
923 send_message(MSG_SETMETADATA
, message
);
925 get_message(message
, REPLY_DONE
);
929 RemoteDatabase::request_document(Xapian::docid did
) const
932 pack_uint(message
, did
);
933 send_message(MSG_REQUESTDOCUMENT
, message
);
935 get_message(message
, REPLY_DONE
);
939 RemoteDatabase::add_spelling(string_view word
,
940 Xapian::termcount freqinc
) const
942 uncommitted_changes
= true;
945 pack_uint(message
, freqinc
);
947 send_message(MSG_ADDSPELLING
, message
);
949 get_message(message
, REPLY_DONE
);
953 RemoteDatabase::remove_spelling(string_view word
,
954 Xapian::termcount freqdec
) const
956 uncommitted_changes
= true;
959 pack_uint(message
, freqdec
);
961 send_message(MSG_REMOVESPELLING
, message
);
963 get_message(message
, REPLY_REMOVESPELLING
);
964 const char * p
= message
.data();
965 const char * p_end
= p
+ message
.size();
966 Xapian::termcount result
;
967 if (!unpack_uint_last(&p
, p_end
, &result
)) {
968 throw Xapian::NetworkError("Bad REPLY_REMOVESPELLING",
975 RemoteDatabase::open_synonym_termlist(string_view word
) const
978 send_message(MSG_SYNONYMTERMLIST
, word
);
979 get_message(message
, REPLY_SYNONYMTERMLIST
);
980 return new RemoteKeyList({}, std::move(message
));
984 RemoteDatabase::open_synonym_keylist(string_view prefix
) const
987 send_message(MSG_SYNONYMKEYLIST
, prefix
);
988 get_message(message
, REPLY_SYNONYMKEYLIST
);
989 return new RemoteKeyList({}, std::move(message
));
993 RemoteDatabase::add_synonym(string_view word
, string_view synonym
) const
995 uncommitted_changes
= true;
998 pack_string(message
, word
);
1000 send_message(MSG_ADDSYNONYM
, message
);
1001 get_message(message
, REPLY_DONE
);
1005 RemoteDatabase::remove_synonym(string_view word
, string_view synonym
) const
1007 uncommitted_changes
= true;
1010 pack_string(message
, word
);
1012 send_message(MSG_REMOVESYNONYM
, message
);
1013 get_message(message
, REPLY_DONE
);
1017 RemoteDatabase::clear_synonyms(string_view word
) const
1019 uncommitted_changes
= true;
1022 send_message(MSG_CLEARSYNONYMS
, word
);
1023 get_message(message
, REPLY_DONE
);
1027 RemoteDatabase::locked() const
1029 throw Xapian::UnimplementedError("Database::locked() not implemented for remote backend");
1033 RemoteDatabase::reconstruct_text(Xapian::docid did
,
1036 Xapian::termpos start_pos
,
1037 Xapian::termpos end_pos
) const
1040 pack_uint(message
, did
);
1041 pack_uint(message
, length
);
1042 pack_uint(message
, start_pos
);
1043 pack_uint(message
, end_pos
);
1045 send_message(MSG_RECONSTRUCTTEXT
, message
);
1047 get_message(message
, REPLY_RECONSTRUCTTEXT
);
1052 RemoteDatabase::get_description() const
1054 string desc
= "Remote(context=";
1055 desc
+= link
.get_context();