xapian-core  2.0.0
glass_database.cc
Go to the documentation of this file.
1 
4 /* Copyright 1999,2000,2001 BrightStation PLC
5  * Copyright 2001 Hein Ragas
6  * Copyright 2002 Ananova Ltd
7  * Copyright 2002-2024 Olly Betts
8  * Copyright 2006,2008 Lemur Consulting Ltd
9  * Copyright 2009 Richard Boulton
10  * Copyright 2009 Kan-Ru Chen
11  * Copyright 2011 Dan Colish
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License as
15  * published by the Free Software Foundation; either version 2 of the
16  * License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, see
25  * <https://www.gnu.org/licenses/>.
26  */
27 
28 #include <config.h>
29 
30 #include "glass_database.h"
31 
32 #include "xapian/constants.h"
33 #include "xapian/error.h"
34 #include "xapian/valueiterator.h"
35 
37 #include "glass_alldocspostlist.h"
38 #include "glass_alltermslist.h"
39 #include "glass_defs.h"
40 #include "glass_docdata.h"
41 #include "glass_document.h"
42 #include "../flint_lock.h"
43 #include "glass_metadata.h"
44 #include "glass_positionlist.h"
45 #include "glass_postlist.h"
48 #include "glass_termlist.h"
49 #include "glass_valuelist.h"
50 #include "glass_values.h"
51 #include "debuglog.h"
52 #include "fd.h"
53 #include "filetests.h"
54 #include "io_utils.h"
55 #include "pack.h"
56 #include "parseint.h"
57 #include "net/remoteconnection.h"
58 #include "api/replication.h"
59 #include "replicationprotocol.h"
60 #include "posixy_wrapper.h"
61 #include "str.h"
62 #include "stringutils.h"
63 #include "backends/valuestats.h"
64 
65 #include "safesysstat.h"
66 #include <sys/types.h>
67 
68 #include <algorithm>
69 #include <cerrno>
70 #include <cstdlib>
71 #include <memory>
72 #include <string>
73 
74 using namespace std;
75 using namespace Xapian;
77 
78 // The maximum safe term length is determined by the postlist. There we
79 // store the term using pack_string_preserving_sort() which takes the
80 // length of the string plus an extra byte (assuming the string doesn't
81 // contain any zero bytes), followed by the docid with encoded with
82 // pack_uint_preserving_sort() which takes up to 5 bytes (for a 32-bit
83 // docid).
84 //
85 // The Btree manager's key length limit is 255 bytes so the maximum safe term
86 // length is 255 - 1 - 5 = 249 bytes. We actually set the limit at 245 for
87 // consistency with flint and chert, and also because this allows for 64-bit
88 // docids.
89 //
90 // If the term contains zero bytes, the limit is lower (by one for each zero
91 // byte in the term).
92 #define MAX_SAFE_TERM_LENGTH 245
93 
94 /* This opens the tables, determining the current and next revision numbers,
95  * and stores handles to the tables.
96  */
97 GlassDatabase::GlassDatabase(string_view glass_dir, int flags,
98  unsigned int block_size)
99  : Xapian::Database::Internal(flags == Xapian::DB_READONLY_ ?
100  TRANSACTION_READONLY :
101  TRANSACTION_NONE),
102  db_dir(glass_dir),
103  readonly(flags == Xapian::DB_READONLY_),
104  version_file(db_dir),
105  postlist_table(db_dir, readonly),
106  position_table(db_dir, readonly),
107  // Note: (Xapian::DB_READONLY_ & Xapian::DB_NO_TERMLIST) is true,
108  // so opening to read we always permit the termlist to be missing.
109  termlist_table(db_dir, readonly, (flags & Xapian::DB_NO_TERMLIST)),
110  value_manager(&postlist_table, &termlist_table),
111  synonym_table(db_dir, readonly),
112  spelling_table(db_dir, readonly),
113  docdata_table(db_dir, readonly),
114  lock(db_dir),
115  changes(db_dir)
116 {
117  LOGCALL_CTOR(DB, "GlassDatabase", glass_dir | flags | block_size);
118 
119  if (readonly) {
120  open_tables(flags);
121  return;
122  }
123 
124  // Block size must in the range GLASS_MIN_BLOCKSIZE..GLASS_MAX_BLOCKSIZE
125  // and a power of two.
126  if (block_size < GLASS_MIN_BLOCKSIZE ||
127  block_size > GLASS_MAX_BLOCKSIZE ||
128  (block_size & (block_size - 1)) != 0) {
129  block_size = GLASS_DEFAULT_BLOCKSIZE;
130  }
131 
132  int action = flags & Xapian::DB_ACTION_MASK_;
133  if (action != Xapian::DB_OPEN && !database_exists()) {
134  // Create the directory for the database, if it doesn't exist
135  // already.
136  if (mkdir(db_dir.c_str(), 0755) < 0) {
137  int mkdir_errno = errno;
138  if (mkdir_errno != EEXIST || !dir_exists(db_dir)) {
139  throw Xapian::DatabaseCreateError(db_dir + ": mkdir failed",
140  mkdir_errno);
141  }
142  }
143 
144  get_database_write_lock(flags, true);
145 
146  create_and_open_tables(flags, block_size);
147  return;
148  }
149 
150  if (action == Xapian::DB_CREATE) {
151  throw Xapian::DatabaseCreateError("Can't create new database at '" +
152  db_dir + "': a database already exists and I was told "
153  "not to overwrite it");
154  }
155 
156  get_database_write_lock(flags, false);
157  // if we're overwriting, pretend the db doesn't exist
158  if (action == Xapian::DB_CREATE_OR_OVERWRITE) {
159  create_and_open_tables(flags, block_size);
160  return;
161  }
162 
163  // Open the latest version of each table.
164  open_tables(flags);
165 }
166 
168  : Xapian::Database::Internal(TRANSACTION_READONLY),
169  db_dir(),
170  readonly(true),
171  version_file(fd),
172  postlist_table(fd, version_file.get_offset(), readonly),
173  position_table(fd, version_file.get_offset(), readonly),
174  termlist_table(fd, version_file.get_offset(), readonly, true),
175  value_manager(&postlist_table, &termlist_table),
176  synonym_table(fd, version_file.get_offset(), readonly),
177  spelling_table(fd, version_file.get_offset(), readonly),
178  docdata_table(fd, version_file.get_offset(), readonly),
179  lock(),
180  changes(string())
181 {
182  LOGCALL_CTOR(DB, "GlassDatabase", fd);
183  open_tables(Xapian::DB_READONLY_);
184 }
185 
187 {
188  LOGCALL_DTOR(DB, "GlassDatabase");
189 }
190 
191 bool
193  LOGCALL(DB, bool, "GlassDatabase::database_exists", NO_ARGS);
194  // The postlist table is the only non-optional one.
196 }
197 
198 void
199 GlassDatabase::create_and_open_tables(int flags, unsigned int block_size)
200 {
201  LOGCALL_VOID(DB, "GlassDatabase::create_and_open_tables", flags|block_size);
202  // The caller is expected to create the database directory if it doesn't
203  // already exist.
204 
206  v.create(block_size);
207 
209  const string& tmpfile = v.write(rev, flags);
210 
217 
218  if (!v.sync(tmpfile, rev, flags)) {
219  throw Xapian::DatabaseCreateError("Failed to create iamglass file");
220  }
221 
223 }
224 
225 bool
227 {
228  LOGCALL(DB, bool, "GlassDatabase::open_tables", flags);
229 
231 
232  if (cur_rev != 0) {
233  // We're reopening, so ensure that we throw DatabaseError if close()
234  // was called. It could be argued that reopen() can be a no-op in this
235  // case, but that's confusing if someone expects that reopen() can undo
236  // the effects of close() - in fact reopen() is closer to
237  // update_reader_to_latest_revision().
238  if (!postlist_table.is_open())
240  }
241 
242  version_file.read();
244  if (cur_rev && cur_rev == rev) {
245  // We're reopening a database and the revision hasn't changed so we
246  // don't need to do anything.
247  RETURN(false);
248  }
249 
256 
259 
261 
262  if (!readonly) {
265  GlassChanges * p = changes.start(revision, revision + 1, flags);
273  }
274  return true;
275 }
276 
279 {
280  LOGCALL(DB, glass_revision_number_t, "GlassDatabase::get_next_revision_number", NO_ARGS);
281  // FIXME: If we permit a revision before the latest and then updating it
282  // (to roll back more recent changes) then we (probably) need this to be
283  // one more than the *highest* revision previously committed.
285 }
286 
287 void
289  glass_revision_number_t * startrev,
290  glass_revision_number_t * endrev) const
291 {
292  FD fd(posixy_open(path.c_str(), O_RDONLY | O_CLOEXEC));
293  if (fd < 0) {
294  string message = string("Couldn't open changeset ") +
295  path + " to read";
296  throw Xapian::DatabaseError(message, errno);
297  }
298 
299  char buf[REASONABLE_CHANGESET_SIZE];
300  const char *start = buf;
301  const char *end = buf + io_read(fd, buf, REASONABLE_CHANGESET_SIZE);
302  if (size_t(end - start) < CONST_STRLEN(CHANGES_MAGIC_STRING))
303  throw Xapian::DatabaseError("Changeset too short at " + path);
304  if (memcmp(start, CHANGES_MAGIC_STRING,
306  string message = string("Changeset at ") +
307  path + " does not contain valid magic string";
308  throw Xapian::DatabaseError(message);
309  }
311 
312  unsigned int changes_version;
313  if (!unpack_uint(&start, end, &changes_version))
314  throw Xapian::DatabaseError("Couldn't read a valid version number for "
315  "changeset at " + path);
316  if (changes_version != CHANGES_VERSION)
317  throw Xapian::DatabaseError("Don't support version of changeset at " +
318  path);
319 
320  if (!unpack_uint(&start, end, startrev))
321  throw Xapian::DatabaseError("Couldn't read a valid start revision from "
322  "changeset at " + path);
323 
324  if (!unpack_uint(&start, end, endrev))
325  throw Xapian::DatabaseError("Couldn't read a valid end revision for "
326  "changeset at " + path);
327 }
328 
329 void
331 {
332  LOGCALL_VOID(DB, "GlassDatabase::set_revision_number", flags|new_revision);
333 
335  if (new_revision <= rev && rev != 0) {
336  string m = "New revision ";
337  m += str(new_revision);
338  m += " <= old revision ";
339  m += str(rev);
340  throw Xapian::DatabaseError(m);
341  }
342 
344 
351 
358 
359  const string & tmpfile = version_file.write(new_revision, flags);
360  if (!postlist_table.sync() ||
361  !position_table.sync() ||
362  !termlist_table.sync() ||
363  !synonym_table.sync() ||
364  !spelling_table.sync() ||
365  !docdata_table.sync() ||
366  !version_file.sync(tmpfile, new_revision, flags)) {
367  int saved_errno = errno;
368  (void)unlink(tmpfile.c_str());
369  throw Xapian::DatabaseError("Commit failed", saved_errno);
370  }
371 
372  changes.commit(new_revision, flags);
373 }
374 
375 void
377 {
379 }
380 
381 void
383 {
385  for (t = query.get_unique_terms_begin(); t != Xapian::TermIterator(); ++t) {
386  const string & term = *t;
388  break;
389  }
390 }
391 
392 bool
394 {
395  LOGCALL(DB, bool, "GlassDatabase::reopen", NO_ARGS);
396  if (!readonly) RETURN(false);
398 }
399 
400 void
402 {
403  LOGCALL_VOID(DB, "GlassDatabase::close", NO_ARGS);
404  postlist_table.close(true);
405  position_table.close(true);
406  termlist_table.close(true);
407  synonym_table.close(true);
408  spelling_table.close(true);
409  docdata_table.close(true);
410  lock.release();
411 }
412 
413 void
414 GlassDatabase::get_database_write_lock(int flags, bool creating)
415 {
416  LOGCALL_VOID(DB, "GlassDatabase::get_database_write_lock", flags|creating);
417  // FIXME: Handle Xapian::DB_DANGEROUS here, perhaps by having readers
418  // get a lock on the revision they're reading, and then requiring the
419  // writer get an exclusive lock in this case.
420  string explanation;
421  bool retry = flags & Xapian::DB_RETRY_LOCK;
422  FlintLock::reason why = lock.lock(true, retry, explanation);
423  if (why != FlintLock::SUCCESS) {
424  if (why == FlintLock::UNKNOWN && !creating && !database_exists()) {
425  string msg("No glass database found at path '");
426  msg += db_dir;
427  msg += '\'';
429  }
430  lock.throw_databaselockerror(why, db_dir, explanation);
431  }
432 }
433 
434 void
436 {
437  LOGCALL_VOID(DB, "GlassDatabase::send_whole_database", conn | end_time);
438 #ifdef XAPIAN_HAS_REMOTE_BACKEND
439  // Send the current revision number in the header.
440  string buf;
441  pack_string(buf, get_uuid());
442  pack_uint(buf, get_revision());
444 
445  // Send all the tables. The tables which we want to be cached best after
446  // the copy finishes are sent last.
447  static const char filenames[] =
448  "termlist." GLASS_TABLE_EXTENSION "\0"
449  "synonym." GLASS_TABLE_EXTENSION "\0"
450  "spelling." GLASS_TABLE_EXTENSION "\0"
451  "docdata." GLASS_TABLE_EXTENSION "\0"
452  "position." GLASS_TABLE_EXTENSION "\0"
453  "postlist." GLASS_TABLE_EXTENSION "\0"
454  "iamglass\0";
455  string filepath = db_dir;
456  filepath += '/';
457  const char * p = filenames;
458  do {
459  size_t len = strlen(p);
460  filepath.replace(db_dir.size() + 1, string::npos, p, len);
461  FD fd(posixy_open(filepath.c_str(), O_RDONLY | O_CLOEXEC));
462  if (fd >= 0) {
465  }
466  p += len + 1;
467  } while (*p);
468 #else
469  (void)conn;
470  (void)end_time;
471 #endif
472 }
473 
474 void
476  string_view revision,
477  bool need_whole_db,
478  ReplicationInfo * info)
479 {
480  LOGCALL_VOID(DB, "GlassDatabase::write_changesets_to_fd", fd | revision | need_whole_db | info);
481 #ifdef XAPIAN_HAS_REMOTE_BACKEND
482  int whole_db_copies_left = MAX_DB_COPIES_PER_CONVERSATION;
483  glass_revision_number_t start_rev_num = 0;
484  string start_uuid = get_uuid();
485 
486  glass_revision_number_t needed_rev_num = 0;
487 
488  const char * rev_ptr = revision.data();
489  const char * rev_end = rev_ptr + revision.size();
490  if (!unpack_uint(&rev_ptr, rev_end, &start_rev_num)) {
491  need_whole_db = true;
492  }
493 
494  RemoteConnection conn(-1, fd, string());
495 
496  // While the starting revision number is less than the latest revision
497  // number, look for a changeset, and write it.
498  //
499  // FIXME - perhaps we should make hardlinks for all the changesets we're
500  // likely to need, first, and then start sending them, so that there's no
501  // risk of them disappearing while we're sending earlier ones.
502  while (true) {
503  if (need_whole_db) {
504  // Decrease the counter of copies left to be sent, and fail
505  // if we've already copied the database enough. This ensures that
506  // synchronisation attempts always terminate eventually.
507  if (whole_db_copies_left == 0) {
509  "Database changing too fast",
510  0.0);
511  return;
512  }
513  whole_db_copies_left--;
514 
515  // Send the whole database across.
516  start_rev_num = get_revision();
517  start_uuid = get_uuid();
518 
519  send_whole_database(conn, 0.0);
520  if (info != NULL)
521  ++(info->fullcopy_count);
522 
523  need_whole_db = false;
524 
525  reopen();
526  if (start_uuid == get_uuid()) {
527  // Send the latest revision number after sending the tables.
528  // The update must proceed to that revision number before the
529  // copy is safe to make live.
530 
531  string buf;
532  needed_rev_num = get_revision();
533  pack_uint(buf, needed_rev_num);
534  conn.send_message(REPL_REPLY_DB_FOOTER, buf, 0.0);
535  if (info != NULL && start_rev_num == needed_rev_num)
536  info->changed = true;
537  } else {
538  // Database has been replaced since we did the copy. Send a
539  // higher revision number than the revision we've just copied,
540  // so that the client doesn't make the copy we've just done
541  // live, and then mark that we need to do a copy again.
542  // The client will never actually get the required revision,
543  // because the next message is going to be the start of a new
544  // database transfer.
545 
546  string buf;
547  pack_uint(buf, start_rev_num + 1);
548  conn.send_message(REPL_REPLY_DB_FOOTER, buf, 0.0);
549  need_whole_db = true;
550  }
551  } else {
552  // Check if we've sent all the updates.
553  if (start_rev_num >= get_revision()) {
554  reopen();
555  if (start_uuid != get_uuid()) {
556  need_whole_db = true;
557  continue;
558  }
559  if (start_rev_num >= get_revision()) {
560  break;
561  }
562  }
563 
564  // Look for the changeset for revision start_rev_num.
565  string changes_name = db_dir + "/changes" + str(start_rev_num);
566  FD fd_changes(posixy_open(changes_name.c_str(), O_RDONLY | O_CLOEXEC));
567  if (fd_changes >= 0) {
568  // Send it, and also update start_rev_num to the new value
569  // specified in the changeset.
570  glass_revision_number_t changeset_start_rev_num;
571  glass_revision_number_t changeset_end_rev_num;
572  get_changeset_revisions(changes_name,
573  &changeset_start_rev_num,
574  &changeset_end_rev_num);
575  if (changeset_start_rev_num != start_rev_num) {
576  throw Xapian::DatabaseError("Changeset start revision does not match changeset filename");
577  }
578  if (changeset_start_rev_num >= changeset_end_rev_num) {
579  throw Xapian::DatabaseError("Changeset start revision is not less than end revision");
580  }
581 
582  conn.send_file(REPL_REPLY_CHANGESET, fd_changes, 0.0);
583  start_rev_num = changeset_end_rev_num;
584  if (info != NULL) {
585  ++(info->changeset_count);
586  if (start_rev_num >= needed_rev_num)
587  info->changed = true;
588  }
589  } else {
590  // The changeset doesn't exist: leave the revision number as it
591  // is, and mark for doing a full database copy.
592  need_whole_db = true;
593  }
594  }
595  }
597 #else
598  (void)fd;
599  (void)revision;
600  (void)need_whole_db;
601  (void)info;
602 #endif
603 }
604 
605 void
607  const std::string & msg)
608 {
609  // Modifications failed. Wipe all the modifications from memory.
610  int flags = postlist_table.get_flags();
612  try {
613  // Discard any buffered changes and reinitialised cached values
614  // from the table.
615  cancel();
616 
617  // Reopen tables with old revision number.
618  version_file.read();
619  docdata_table.open(flags, version_file.get_root(Glass::DOCDATA), old_revision);
621  synonym_table.open(flags, version_file.get_root(Glass::SYNONYM), old_revision);
625 
627  } catch (const Xapian::Error &e) {
628  // We failed to roll-back so close the database to avoid the risk of
629  // database corruption.
631  throw Xapian::DatabaseError("Modifications failed (" + msg + "), "
632  "and couldn't open at the old revision: " +
633  e.get_msg());
634  }
635 
636  GlassChanges * p;
637  p = changes.start(old_revision, new_revision, flags);
645 }
646 
647 void
649 {
650  LOGCALL_VOID(DB, "GlassDatabase::apply", NO_ARGS);
651  if (!postlist_table.is_modified() &&
658  return;
659  }
660 
662 
663  int flags = postlist_table.get_flags();
664  try {
665  set_revision_number(flags, new_revision);
666  } catch (const Xapian::Error &e) {
667  modifications_failed(new_revision, e.get_description());
668  throw;
669  } catch (...) {
670  modifications_failed(new_revision, "Unknown error");
671  throw;
672  }
673 
674  GlassChanges * p;
675  p = changes.start(new_revision, new_revision + 1, flags);
683 }
684 
685 void
687 {
688  LOGCALL_VOID(DB, "GlassDatabase::cancel", NO_ARGS);
698 
701 }
702 
705 {
706  LOGCALL(DB, Xapian::doccount, "GlassDatabase::get_doccount", NO_ARGS);
708 }
709 
712 {
713  LOGCALL(DB, Xapian::docid, "GlassDatabase::get_lastdocid", NO_ARGS);
715 }
716 
719 {
720  LOGCALL(DB, Xapian::totallength, "GlassDatabase::get_total_length", NO_ARGS);
722 }
723 
726 {
727  LOGCALL(DB, Xapian::termcount, "GlassDatabase::get_doclength", did);
728  Assert(did != 0);
729  intrusive_ptr<const GlassDatabase> ptrtothis(this);
730  RETURN(postlist_table.get_doclength(did, ptrtothis));
731 }
732 
735 {
736  LOGCALL(DB, Xapian::termcount, "GlassDatabase::get_unique_terms", did);
737  Assert(did != 0);
738  intrusive_ptr<const GlassDatabase> ptrtothis(this);
739  RETURN(GlassTermList(ptrtothis, did).get_unique_terms());
740 }
741 
744 {
745  LOGCALL(DB, Xapian::termcount, "GlassDatabase::get_wdfdocmax", did);
746  Assert(did != 0);
747  intrusive_ptr<const GlassDatabase> ptrtothis(this);
748  GlassTermList termlist(ptrtothis, did);
749  Xapian::termcount max_wdf = 0;
750  while (termlist.next() == NULL) {
751  Xapian::termcount current_wdf = termlist.get_wdf();
752  if (current_wdf > max_wdf) max_wdf = current_wdf;
753  }
754  RETURN(max_wdf);
755 }
756 
757 void
759  Xapian::doccount * termfreq_ptr,
760  Xapian::termcount * collfreq_ptr) const
761 {
762  LOGCALL_VOID(DB, "GlassDatabase::get_freqs", term | termfreq_ptr | collfreq_ptr);
763  Assert(!term.empty());
764  postlist_table.get_freqs(term, termfreq_ptr, collfreq_ptr);
765 }
766 
769 {
770  LOGCALL(DB, Xapian::doccount, "GlassDatabase::get_value_freq", slot);
772 }
773 
774 std::string
776 {
777  LOGCALL(DB, std::string, "GlassDatabase::get_value_lower_bound", slot);
779 }
780 
781 std::string
783 {
784  LOGCALL(DB, std::string, "GlassDatabase::get_value_upper_bound", slot);
786 }
787 
790 {
792 }
793 
796 {
798 }
799 
802 {
803  Assert(!term.empty());
804  Xapian::termcount wdfub;
805  postlist_table.get_freqs(term, NULL, NULL, &wdfub);
806  return min(wdfub, version_file.get_wdf_upper_bound());
807 }
808 
811 {
813 }
814 
815 bool
817 {
818  LOGCALL(DB, bool, "GlassDatabase::term_exists", term);
819  if (term.empty()) {
820  RETURN(get_doccount() != 0);
821  }
823 }
824 
825 bool
827 {
828  return !position_table.empty();
829 }
830 
831 PostList *
833 {
834  LOGCALL(DB, PostList *, "GlassDatabase::open_post_list", term);
836 }
837 
839 GlassDatabase::open_leaf_post_list(string_view term, bool need_read_pos) const
840 {
841  LOGCALL(DB, LeafPostList *, "GlassDatabase::open_leaf_post_list", term | need_read_pos);
842  (void)need_read_pos;
843  intrusive_ptr<const GlassDatabase> ptrtothis(this);
844 
845  if (term.empty()) {
846  Assert(!need_read_pos);
848  if (rare(doccount == 0)) {
849  RETURN(nullptr);
850  }
853  }
854  RETURN(new GlassAllDocsPostList(ptrtothis, doccount));
855  }
856 
857  auto pl = new GlassPostList(ptrtothis, term, true);
858  if (pl->get_termfreq() == 0) {
859  delete pl;
860  pl = nullptr;
861  }
862  RETURN(pl);
863 }
864 
865 ValueList *
867 {
868  LOGCALL(DB, ValueList *, "GlassDatabase::open_value_list", slot);
869  intrusive_ptr<const GlassDatabase> ptrtothis(this);
870  RETURN(new GlassValueList(slot, ptrtothis));
871 }
872 
873 TermList *
875 {
876  LOGCALL(DB, TermList *, "GlassDatabase::open_term_list", did);
877  Assert(did != 0);
878  if (!termlist_table.is_open())
880  intrusive_ptr<const GlassDatabase> ptrtothis(this);
881  RETURN(new GlassTermList(ptrtothis, did));
882 }
883 
884 TermList *
886 {
887  return GlassDatabase::open_term_list(did);
888 }
889 
892 {
893  LOGCALL(DB, Xapian::Document::Internal *, "GlassDatabase::open_document", did | lazy);
894  Assert(did != 0);
895  if (!lazy) {
896  // This will throw DocNotFoundError if the document doesn't exist.
897  (void)get_doclength(did);
898  }
899 
901  RETURN(new GlassDocument(ptrtothis, did, &value_manager, &docdata_table));
902 }
903 
904 void
906  Xapian::docid did,
907  string_view term) const
908 {
909  Assert(did != 0);
910  pos_list->read_data(did, term);
911 }
912 
915 {
917 }
918 
919 PositionList *
921 {
922  Assert(did != 0);
924 }
925 
926 TermList *
927 GlassDatabase::open_allterms(string_view prefix) const
928 {
929  LOGCALL(DB, TermList*, "GlassDatabase::open_allterms", prefix);
931  prefix));
932 }
933 
934 TermList*
936 {
937  return spelling_table.open_termlist(word);
938 }
939 
940 TermList *
942 {
944  if (!cursor) return NULL;
946  cursor);
947 }
948 
951 {
952  return spelling_table.get_word_frequency(word);
953 }
954 
955 TermList *
957 {
959 }
960 
961 TermList *
962 GlassDatabase::open_synonym_keylist(string_view prefix) const
963 {
964  GlassCursor * cursor = synonym_table.cursor_get();
965  if (!cursor) return NULL;
967  cursor, prefix);
968 }
969 
970 string
971 GlassDatabase::get_metadata(string_view key) const
972 {
973  LOGCALL(DB, string, "GlassDatabase::get_metadata", key);
974  string btree_key("\x00\xc0", 2);
975  btree_key += key;
976  string tag;
977  (void)postlist_table.get_exact_entry(btree_key, tag);
978  RETURN(tag);
979 }
980 
981 TermList*
982 GlassDatabase::open_metadata_keylist(std::string_view prefix) const
983 {
984  LOGCALL(DB, TermList*, "GlassDatabase::open_metadata_keylist", prefix);
986  if (!cursor) RETURN(NULL);
988  cursor, prefix));
989 }
990 
993 {
994  LOGCALL(DB, Xapian::rev, "GlassDatabase::get_revision", NO_ARGS);
996 }
997 
998 string
1000 {
1001  LOGCALL(DB, string, "GlassDatabase::get_uuid", NO_ARGS);
1003 }
1004 
1005 void
1007 {
1008  // Either the database has been closed, or else there's no termlist table.
1009  // Check if the postlist table is open to determine which is the case.
1010  if (!postlist_table.is_open())
1012  throw Xapian::FeatureUnavailableError("Database has no termlist");
1013 }
1014 
1015 void
1017  Xapian::docid & last) const
1018 {
1019  last = version_file.get_last_docid();
1020  if (last == version_file.get_doccount()) {
1021  // Contiguous range starting at 1.
1022  first = 1;
1023  return;
1024  }
1025  postlist_table.get_used_docid_range(first, last);
1026 }
1027 
1028 bool
1030 {
1031  return false;
1032 }
1033 
1034 bool
1036 {
1037  return lock.test();
1038 }
1039 
1042 {
1043  if (!postlist_table.is_open())
1045 
1046  if (flags == Xapian::DB_READONLY_) return this;
1047  // Mask out backend type bits as these don't make sense here, and set the
1048  // action to DB_OPEN since we don't want to create or overwrite here.
1049  flags &= ~(DB_ACTION_MASK_ | DB_BACKEND_MASK_);
1050  flags |= Xapian::DB_OPEN;
1051  return new GlassWritableDatabase(db_dir, Xapian::DB_OPEN, flags);
1052 }
1053 
1054 string
1056 {
1057  string desc = "Glass(";
1058  if (!readonly) {
1059  desc += "writable, ";
1060  }
1061  desc += db_dir;
1062  desc += ')';
1063  return desc;
1064 }
1065 
1067 
1069  int block_size)
1070  : GlassDatabase(dir, flags, block_size),
1071  change_count(0),
1072  flush_threshold(0),
1073  modify_shortcut_document(NULL),
1074  modify_shortcut_docid(0)
1075 {
1076  LOGCALL_CTOR(DB, "GlassWritableDatabase", dir | flags | block_size);
1077 
1078  const char *p = getenv("XAPIAN_FLUSH_THRESHOLD");
1079  if (p && *p) {
1080  if (!parse_unsigned(p, flush_threshold)) {
1081  throw Xapian::InvalidArgumentError("XAPIAN_FLUSH_THRESHOLD must "
1082  "be a non-negative integer");
1083  }
1084  }
1085  if (flush_threshold == 0)
1086  flush_threshold = 10000;
1087 }
1088 
1090 {
1091  LOGCALL_DTOR(DB, "GlassWritableDatabase");
1092  dtor_called();
1093 }
1094 
1095 void
1097 {
1098  if (transaction_active())
1099  throw Xapian::InvalidOperationError("Can't commit during a transaction");
1101  apply();
1102 }
1103 
1104 void
1106 {
1107  // FIXME: this should be done by checking memory usage, not the number of
1108  // changes. We could also look at the amount of data the inverter object
1109  // currently holds.
1110  if (++change_count >= flush_threshold) {
1112  if (!transaction_active()) apply();
1113  }
1114 }
1115 
1116 void
1118 {
1119  try {
1123 
1124  change_count = 0;
1125  } catch (...) {
1126  try {
1128  } catch (...) {
1129  }
1130  throw;
1131  }
1132 }
1133 
1134 void
1136 {
1137  LOGCALL_VOID(DB, "GlassWritableDatabase::close", NO_ARGS);
1138  if (!transaction_active()) {
1139  commit();
1140  // FIXME: if commit() throws, should we still close?
1141  }
1143 }
1144 
1145 void
1147 {
1150 }
1151 
1154 {
1155  LOGCALL(DB, Xapian::docid, "GlassWritableDatabase::add_document", document);
1156  // Make sure the docid counter doesn't overflow.
1158  throw Xapian::DatabaseError("Run out of docids - you'll have to use copydatabase to eliminate any gaps before you can add more documents");
1159  // Use the next unused document ID.
1161 }
1162 
1165  const Xapian::Document & document)
1166 {
1167  LOGCALL(DB, Xapian::docid, "GlassWritableDatabase::add_document_", did | document);
1168  Assert(did != 0);
1169  try {
1170  // Set the document data.
1171  docdata_table.replace_document_data(did, document.get_data());
1172 
1173  // Set the values.
1174  value_manager.add_document(did, document, value_stats);
1175 
1176  Xapian::termcount new_doclen = 0;
1177  {
1179  for ( ; term != document.termlist_end(); ++term) {
1180  termcount wdf = term.get_wdf();
1181  // Calculate the new document length
1182  new_doclen += wdf;
1183  version_file.check_wdf(wdf);
1184 
1185  string tname = *term;
1186  if (tname.size() > MAX_SAFE_TERM_LENGTH)
1187  throw Xapian::InvalidArgumentError("Term too long (> " STRINGIZE(MAX_SAFE_TERM_LENGTH) "): " + tname);
1188 
1189  inverter.add_posting(did, tname, wdf);
1191  }
1192  }
1193  LOGLINE(DB, "Calculated doclen for new document " << did << " as " << new_doclen);
1194 
1195  // Set the termlist.
1196  if (termlist_table.is_open())
1197  termlist_table.set_termlist(did, document, new_doclen);
1198 
1199  // Set the new document length
1200  inverter.set_doclength(did, new_doclen, true);
1201  version_file.add_document(new_doclen);
1202  } catch (...) {
1203  // If an error occurs while adding a document, or doing any other
1204  // transaction, the modifications so far must be cleared before
1205  // returning control to the user - otherwise partial modifications will
1206  // persist in memory, and eventually get written to disk.
1207  cancel();
1208  throw;
1209  }
1210 
1212 
1213  RETURN(did);
1214 }
1215 
1216 void
1218 {
1219  LOGCALL_VOID(DB, "GlassWritableDatabase::delete_document", did);
1220  Assert(did != 0);
1221 
1222  if (!termlist_table.is_open())
1224 
1225  // Remove the document data. If this fails, just propagate the exception since
1226  // the state should still be consistent.
1227  bool doc_really_existed = docdata_table.delete_document_data(did);
1228 
1229  if (rare(modify_shortcut_docid == did)) {
1230  // The modify_shortcut document can't be used for a modification
1231  // shortcut now, because it's been deleted!
1232  modify_shortcut_document = NULL;
1234  doc_really_existed = true;
1235  }
1236 
1237  if (!doc_really_existed) {
1238  // Ensure we throw DocumentNotFound if the document doesn't exist.
1239  (void)get_doclength(did);
1240  }
1241 
1242  try {
1243  // Remove the values.
1245 
1246  // OK, now add entries to remove the postings in the underlying record.
1248  GlassTermList termlist(ptrtothis, did);
1249 
1251 
1252  while (termlist.next() == NULL) {
1253  string tname = termlist.get_termname();
1254  inverter.delete_positionlist(did, tname);
1255 
1256  inverter.remove_posting(did, tname, termlist.get_wdf());
1257  }
1258 
1259  // Remove the termlist.
1260  if (termlist_table.is_open())
1262 
1263  // Mark this document as removed.
1265  } catch (...) {
1266  // If an error occurs while deleting a document, or doing any other
1267  // transaction, the modifications so far must be cleared before
1268  // returning control to the user - otherwise partial modifications will
1269  // persist in memory, and eventually get written to disk.
1270  cancel();
1271  throw;
1272  }
1273 
1275 }
1276 
1277 void
1279  const Xapian::Document & document)
1280 {
1281  LOGCALL_VOID(DB, "GlassWritableDatabase::replace_document", did | document);
1282  Assert(did != 0);
1283 
1284  try {
1285  if (did > version_file.get_last_docid()) {
1287  // If this docid is above the highwatermark, then we can't be
1288  // replacing an existing document.
1289  (void)add_document_(did, document);
1290  return;
1291  }
1292 
1293  if (!termlist_table.is_open()) {
1294  // We can replace an *unused* docid <= last_docid too.
1295  intrusive_ptr<const GlassDatabase> ptrtothis(this);
1296  if (!postlist_table.document_exists(did, ptrtothis)) {
1297  (void)add_document_(did, document);
1298  return;
1299  }
1301  }
1302 
1303  // Check for a document read from this database being replaced - ie, a
1304  // modification operation.
1305  bool modifying = false;
1306  if (modify_shortcut_docid &&
1307  document.internal->get_docid() == modify_shortcut_docid) {
1308  if (document.internal.get() == modify_shortcut_document) {
1309  // We have a docid, it matches, and the pointer matches, so we
1310  // can skip modification of any data which hasn't been modified
1311  // in the document.
1312  if (!document.internal->modified()) {
1313  // If the document is unchanged, we've nothing to do.
1314  return;
1315  }
1316  modifying = true;
1317  LOGLINE(DB, "Detected potential document modification shortcut.");
1318  } else {
1319  // The modify_shortcut document can't be used for a
1320  // modification shortcut now, because it's about to be
1321  // modified.
1322  modify_shortcut_document = NULL;
1324  }
1325  }
1326 
1327  if (!modifying || document.internal->terms_modified()) {
1328  bool pos_modified = !modifying ||
1329  document.internal->positions_modified();
1330 
1331  // Avoid the cost of throwing and handling an exception if
1332  // there's not already a document with docid did.
1334  GlassTermList termlist(ptrtothis, did, false);
1335  if (termlist.not_present()) {
1336  (void)add_document_(did, document);
1337  return;
1338  }
1339 
1341  Xapian::termcount old_doclen = termlist.get_doclength();
1342  version_file.delete_document(old_doclen);
1343  Xapian::termcount new_doclen = old_doclen;
1344 
1345  string old_tname, new_tname;
1346 
1347  bool termlist_at_end = (termlist.next() != NULL);
1348  while (!termlist_at_end || term != document.termlist_end()) {
1349  int cmp;
1350  if (termlist_at_end) {
1351  cmp = 1;
1352  new_tname = *term;
1353  } else {
1354  old_tname = termlist.get_termname();
1355  if (term != document.termlist_end()) {
1356  new_tname = *term;
1357  cmp = old_tname.compare(new_tname);
1358  } else {
1359  cmp = -1;
1360  }
1361  }
1362 
1363  if (cmp < 0) {
1364  // Term old_tname has been deleted.
1365  termcount old_wdf = termlist.get_wdf();
1366  new_doclen -= old_wdf;
1367  inverter.remove_posting(did, old_tname, old_wdf);
1368  if (pos_modified)
1369  inverter.delete_positionlist(did, old_tname);
1370  termlist_at_end = (termlist.next() != NULL);
1371  } else if (cmp > 0) {
1372  // Term new_tname as been added.
1373  termcount new_wdf = term.get_wdf();
1374  new_doclen += new_wdf;
1375  version_file.check_wdf(new_wdf);
1376  if (new_tname.size() > MAX_SAFE_TERM_LENGTH)
1377  throw Xapian::InvalidArgumentError("Term too long (> " STRINGIZE(MAX_SAFE_TERM_LENGTH) "): " + new_tname);
1378  inverter.add_posting(did, new_tname, new_wdf);
1379  if (pos_modified) {
1380  inverter.set_positionlist(position_table, did, new_tname, term);
1381  }
1382  ++term;
1383  } else if (cmp == 0) {
1384  // Term already exists: look for wdf and positionlist changes.
1385  termcount old_wdf = termlist.get_wdf();
1386  termcount new_wdf = term.get_wdf();
1387 
1388  // Check the stats even if wdf hasn't changed, because if
1389  // this is the only document, the stats will have been
1390  // zeroed.
1391  version_file.check_wdf(new_wdf);
1392 
1393  if (old_wdf != new_wdf) {
1394  new_doclen = new_doclen - old_wdf + new_wdf;
1395  inverter.update_posting(did, new_tname, old_wdf, new_wdf);
1396  }
1397 
1398  if (pos_modified) {
1399  inverter.set_positionlist(position_table, did, new_tname, term, true);
1400  }
1401 
1402  ++term;
1403  termlist_at_end = (termlist.next() != NULL);
1404  }
1405  }
1406  LOGLINE(DB, "Calculated doclen for replacement document " << did << " as " << new_doclen);
1407 
1408  // Set the termlist.
1409  if (termlist_table.is_open())
1410  termlist_table.set_termlist(did, document, new_doclen);
1411 
1412  // Set the new document length
1413  if (new_doclen != old_doclen)
1414  inverter.set_doclength(did, new_doclen, false);
1415  version_file.add_document(new_doclen);
1416  }
1417 
1418  if (!modifying || document.internal->data_modified()) {
1419  // Update the document data.
1420  docdata_table.replace_document_data(did, document.get_data());
1421  }
1422 
1423  if (!modifying || document.internal->values_modified()) {
1424  // Replace the values.
1425  value_manager.replace_document(did, document, value_stats);
1426  }
1427  } catch (...) {
1428  // If an error occurs while replacing a document, or doing any other
1429  // transaction, the modifications so far must be cleared before
1430  // returning control to the user - otherwise partial modifications will
1431  // persist in memory, and eventually get written to disk.
1432  cancel();
1433  throw;
1434  }
1435 
1437 }
1438 
1441 {
1442  LOGCALL(DB, Xapian::Document::Internal *, "GlassWritableDatabase::open_document", did | lazy);
1444  // Store the docid only after open_document() successfully returns, so an
1445  // attempt to open a missing document doesn't overwrite this.
1446  modify_shortcut_docid = did;
1448 }
1449 
1452 {
1453  LOGCALL(DB, Xapian::termcount, "GlassWritableDatabase::get_doclength", did);
1454  Xapian::termcount doclen;
1455  if (inverter.get_doclength(did, doclen))
1456  RETURN(doclen);
1458 }
1459 
1462 {
1463  LOGCALL(DB, Xapian::termcount, "GlassWritableDatabase::get_unique_terms", did);
1464  Assert(did != 0);
1465  // Note that the "approximate" size should be exact in this case.
1466  //
1467  // get_unique_terms() really ought to only count terms with wdf > 0, but
1468  // that's expensive to calculate on demand, so for now let's just ensure
1469  // unique_terms <= doclen.
1470  Xapian::termcount doclen;
1471  if (inverter.get_doclength(did, doclen)) {
1472  intrusive_ptr<const GlassDatabase> ptrtothis(this);
1473  GlassTermList termlist(ptrtothis, did);
1474  RETURN(min(doclen, termlist.get_approx_size()));
1475  }
1477 }
1478 
1479 void
1481  Xapian::doccount * termfreq_ptr,
1482  Xapian::termcount * collfreq_ptr) const
1483 {
1484  LOGCALL_VOID(DB, "GlassWritableDatabase::get_freqs", term | termfreq_ptr | collfreq_ptr);
1485  Assert(!term.empty());
1486  GlassDatabase::get_freqs(term, termfreq_ptr, collfreq_ptr);
1487  Xapian::termcount tf_delta, cf_delta;
1488  if (inverter.get_deltas(term, tf_delta, cf_delta)) {
1489  if (termfreq_ptr)
1490  UNSIGNED_OVERFLOW_OK(*termfreq_ptr += tf_delta);
1491  if (collfreq_ptr)
1492  UNSIGNED_OVERFLOW_OK(*collfreq_ptr += cf_delta);
1493  }
1494 }
1495 
1498 {
1499  LOGCALL(DB, Xapian::doccount, "GlassWritableDatabase::get_value_freq", slot);
1500  map<Xapian::valueno, ValueStats>::const_iterator i;
1501  i = value_stats.find(slot);
1502  if (i != value_stats.end()) RETURN(i->second.freq);
1504 }
1505 
1506 std::string
1508 {
1509  LOGCALL(DB, std::string, "GlassWritableDatabase::get_value_lower_bound", slot);
1510  map<Xapian::valueno, ValueStats>::const_iterator i;
1511  i = value_stats.find(slot);
1512  if (i != value_stats.end()) RETURN(i->second.lower_bound);
1514 }
1515 
1516 std::string
1518 {
1519  LOGCALL(DB, std::string, "GlassWritableDatabase::get_value_upper_bound", slot);
1520  map<Xapian::valueno, ValueStats>::const_iterator i;
1521  i = value_stats.find(slot);
1522  if (i != value_stats.end()) RETURN(i->second.upper_bound);
1524 }
1525 
1526 bool
1528 {
1529  LOGCALL(DB, bool, "GlassWritableDatabase::term_exists", term);
1530  if (term.empty()) {
1531  RETURN(get_doccount() != 0);
1532  }
1533  Xapian::doccount tf;
1534  get_freqs(term, &tf, NULL);
1535  RETURN(tf != 0);
1536 }
1537 
1538 bool
1540 {
1542 }
1543 
1544 PostList *
1546 {
1547  LOGCALL(DB, PostList *, "GlassWritableDatabase::open_post_list", term);
1549 }
1550 
1551 LeafPostList *
1553  bool need_read_pos) const
1554 {
1555  LOGCALL(DB, LeafPostList *, "GlassWritableDatabase::open_leaf_post_list", term | need_read_pos);
1556  (void)need_read_pos;
1558 
1559  if (term.empty()) {
1560  Assert(!need_read_pos);
1562  if (rare(doccount == 0)) {
1563  RETURN(nullptr);
1564  }
1567  }
1569  RETURN(new GlassAllDocsPostList(ptrtothis, doccount));
1570  }
1571 
1572  // Flush any buffered changes for this term's postlist so we can just
1573  // iterate from the flushed state.
1575 
1576  auto pl = new GlassPostList(ptrtothis, term, true);
1577  if (pl->get_termfreq() == 0) {
1578  delete pl;
1579  pl = nullptr;
1580  }
1581  RETURN(pl);
1582 }
1583 
1584 ValueList *
1586 {
1587  LOGCALL(DB, ValueList *, "GlassWritableDatabase::open_value_list", slot);
1588  // If there are changes, we don't have code to iterate the modified value
1589  // list so we need to flush (but don't commit - there may be a transaction
1590  // in progress).
1593 }
1594 
1595 void
1597  Xapian::docid did,
1598  string_view term) const
1599 {
1600  Assert(did != 0);
1601  string data;
1602  if (inverter.get_positionlist(did, term, data)) {
1603  pos_list->assign_data(std::move(data));
1604  return;
1605  }
1606  GlassDatabase::read_position_list(pos_list, did, term);
1607 }
1608 
1611  string_view term) const
1612 {
1613  Assert(did != 0);
1614  string data;
1615  if (inverter.get_positionlist(did, term, data)) {
1616  if (data.empty())
1617  return 0;
1618  return position_table.positionlist_count(data);
1619  }
1621 }
1622 
1623 PositionList*
1625  string_view term) const
1626 {
1627  Assert(did != 0);
1628  string data;
1629  if (inverter.get_positionlist(did, term, data)) {
1630  if (data.empty()) return nullptr;
1631  return new GlassPositionList(std::move(data));
1632  }
1634 }
1635 
1636 TermList *
1637 GlassWritableDatabase::open_allterms(string_view prefix) const
1638 {
1639  LOGCALL(DB, TermList*, "GlassWritableDatabase::open_allterms", prefix);
1640  if (change_count) {
1641  // There are changes, and terms may have been added or removed, and so
1642  // we need to flush changes for terms with the specified prefix (but
1643  // don't commit - there may be a transaction in progress).
1645  if (prefix.empty()) {
1646  // We've flushed all the posting list changes, but the positions,
1647  // document lengths and stats haven't been written, so set
1648  // change_count to 1.
1649  // FIXME: Can we handle this better?
1650  change_count = 1;
1651  }
1652  }
1654 }
1655 
1656 void
1658 {
1660  inverter.clear();
1661  value_stats.clear();
1662  change_count = 0;
1663 }
1664 
1665 void
1667  Xapian::termcount freqinc) const
1668 {
1669  spelling_table.add_word(word, freqinc);
1670 }
1671 
1674  Xapian::termcount freqdec) const
1675 {
1676  return spelling_table.remove_word(word, freqdec);
1677 }
1678 
1679 TermList *
1681 {
1684 }
1685 
1686 TermList *
1688 {
1690  return GlassDatabase::open_synonym_keylist(prefix);
1691 }
1692 
1693 void
1695  string_view synonym) const
1696 {
1697  synonym_table.add_synonym(term, synonym);
1698 }
1699 
1700 void
1702  string_view synonym) const
1703 {
1704  synonym_table.remove_synonym(term, synonym);
1705 }
1706 
1707 void
1709 {
1711 }
1712 
1713 void
1714 GlassWritableDatabase::set_metadata(string_view key, string_view value)
1715 {
1716  LOGCALL_VOID(DB, "GlassWritableDatabase::set_metadata", key | value);
1717  string btree_key("\x00\xc0", 2);
1718  btree_key += key;
1719  if (value.empty()) {
1720  postlist_table.del(btree_key);
1721  } else {
1722  postlist_table.add(btree_key, value);
1723  }
1724 }
1725 
1726 void
1728 {
1729  if (obj == modify_shortcut_document) {
1730  modify_shortcut_document = NULL;
1732  }
1733 }
1734 
1735 bool
1737 {
1738  return change_count > 0 ||
1746 }
1747 
1750 {
1751  if (!postlist_table.is_open())
1753 
1754  if (flags != Xapian::DB_READONLY_) {
1755  // Allow changing flags on an open DB.
1756  postlist_table.set_flags(flags);
1757  position_table.set_flags(flags);
1758  termlist_table.set_flags(flags);
1759  synonym_table.set_flags(flags);
1760  spelling_table.set_flags(flags);
1761  docdata_table.set_flags(flags);
1762  return this;
1763  }
1764 
1765  unique_ptr<Database::Internal> result(new GlassDatabase(db_dir));
1766  close();
1767  return result.release();
1768 }
1769 
1770 #ifdef DISABLE_GPL_LIBXAPIAN
1771 # error GPL source we cannot relicense included in libxapian
1772 #endif
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:62
A PostList iterating all docids when they form a contiguous range.
Definition: fd.h:30
void release()
Release the lock.
Definition: flint_lock.cc:458
reason lock(bool exclusive, bool wait, std::string &explanation)
Attempt to obtain the lock.
Definition: flint_lock.cc:124
bool test() const
Test if the lock is held.
Definition: flint_lock.cc:74
void throw_databaselockerror(FlintLock::reason why, const std::string &db_dir, const std::string &explanation) const
Throw Xapian::DatabaseLockError.
Definition: flint_lock.cc:494
GlassChanges * start(glass_revision_number_t old_rev, glass_revision_number_t rev, int flags)
glass_revision_number_t get_oldest_changeset() const
Definition: glass_changes.h:70
void set_oldest_changeset(glass_revision_number_t rev)
Definition: glass_changes.h:66
void commit(glass_revision_number_t new_rev, int flags)
A cursor pointing to a position in a Btree table, for reading several entries in order,...
Definition: glass_cursor.h:148
A backend designed for efficient indexing and retrieval, using compressed posting lists and a btree s...
void get_freqs(std::string_view term, Xapian::doccount *termfreq_ptr, Xapian::termcount *collfreq_ptr) const
Returns frequencies for a term.
void get_used_docid_range(Xapian::docid &first, Xapian::docid &last) const
Find lowest and highest docids actually in use.
string get_metadata(std::string_view key) const
Get the metadata associated with a given key.
TermList * open_term_list(Xapian::docid did) const
friend class GlassPostList
Xapian::totallength get_total_length() const
Return the total length of all documents in this database.
bool locked() const
Return true if the database is open for writing.
Xapian::termcount get_wdf_upper_bound(std::string_view term) const
Get an upper bound on the wdf of term term.
void get_database_write_lock(int flags, bool creating)
Get a write lock on the database, or throw an Xapian::DatabaseLockError if failure.
std::string get_value_lower_bound(Xapian::valueno slot) const
Get a lower bound on the values stored in the given value slot.
std::string get_value_upper_bound(Xapian::valueno slot) const
Get an upper bound on the values stored in the given value slot.
LeafPostList * open_leaf_post_list(std::string_view term, bool need_read_pos) const
Create a LeafPostList for use during a match.
void apply()
Apply any outstanding changes to the tables.
PostList * open_post_list(std::string_view tname) const
Return a PostList suitable for use in a PostingIterator.
void set_revision_number(int flags, glass_revision_number_t new_revision)
Set the revision number in the tables.
ValueList * open_value_list(Xapian::valueno slot) const
Open a value stream.
Xapian::termcount get_doclength_upper_bound() const
Get an upper bound on the length of a document in this DB.
GlassSpellingTable spelling_table
Table storing spelling correction data.
Xapian::Database::Internal * update_lock(int flags)
Lock a read-only database for writing or unlock a writable database.
std::string db_dir
Directory to store databases in.
void cancel()
Cancel any outstanding changes to the tables.
void get_changeset_revisions(const string &path, glass_revision_number_t *startrev, glass_revision_number_t *endrev) const
Get the revision stored in a changeset.
TermList * open_allterms(std::string_view prefix) const
PositionList * open_position_list(Xapian::docid did, std::string_view term) const
TermList * open_metadata_keylist(std::string_view prefix) const
Open a termlist returning each metadata key.
GlassTermListTable termlist_table
Table storing term lists.
Xapian::termcount get_doclength(Xapian::docid did) const
GlassValueManager value_manager
Value manager.
bool term_exists(std::string_view term) const
TermList * open_spelling_termlist(std::string_view word) const
Create a termlist tree from trigrams of word.
Xapian::docid get_lastdocid() const
Return the last used document id of this (sub) database.
Xapian::Document::Internal * open_document(Xapian::docid did, bool lazy) const
Open a handle on a document.
void close()
Close all the tables permanently.
Xapian::doccount get_doccount() const
Virtual methods of Database::Internal.
virtual void read_position_list(GlassRePositionList *pos_list, Xapian::docid did, std::string_view term) const
string get_uuid() const
Get a UUID for the database.
void send_whole_database(RemoteConnection &conn, double end_time)
Send a set of messages which transfer the whole database.
friend class GlassAllTermsList
GlassChanges changes
Replication changesets.
FlintLock lock
Lock object.
GlassVersion version_file
The file describing the Glass database.
void create_and_open_tables(int flags, unsigned int blocksize)
Create new tables, and open them.
void readahead_for_query(const Xapian::Query &query) const
glass_revision_number_t get_next_revision_number() const
Get an object holding the next revision number which should be used in the tables.
friend class GlassSpellingWordsList
Xapian::termcount get_wdfdocmax(Xapian::docid did) const
Get the max wdf in document.
bool readonly
Whether the database is readonly.
void request_document(Xapian::docid) const
Request a document.
virtual Xapian::termcount positionlist_count(Xapian::docid did, std::string_view term) const
TermList * open_spelling_wordlist() const
Return a termlist which returns the words which are spelling correction targets.
Xapian::termcount get_unique_terms_lower_bound() const
Get a lower bound on the unique terms size of a document in this DB.
void modifications_failed(glass_revision_number_t new_revision, const std::string &msg)
Called if a modifications fail.
friend class GlassAllDocsPostList
Xapian::termcount get_unique_terms(Xapian::docid did) const
Get the number of unique terms in document.
GlassDatabase(std::string_view db_dir_, int flags=Xapian::DB_READONLY_, unsigned int block_size=0u)
Create and open a glass database.
virtual bool has_uncommitted_changes() const
Return true if there are uncommitted changes.
std::string get_description() const
Return a string describing this object.
TermList * open_synonym_keylist(std::string_view prefix) const
Open a termlist returning each term which has synonyms.
GlassPositionListTable position_table
Table storing position lists.
bool reopen()
Re-open tables to recover from an overwritten condition, or just get most up-to-date version.
TermList * open_term_list_direct(Xapian::docid did) const
Like open_term_list() but without MultiTermList wrapper.
GlassDocDataTable docdata_table
Table storing document data.
bool database_exists()
Return true if a database exists at the path specified for this database.
Xapian::doccount get_spelling_frequency(std::string_view word) const
Return the number of times word was added as a spelling.
void write_changesets_to_fd(int fd, std::string_view start_revision, bool need_whole_db, Xapian::ReplicationInfo *info)
Write a set of changesets to a file descriptor.
Xapian::termcount get_doclength_lower_bound() const
Get a lower bound on the length of a document in this DB.
TermList * open_synonym_termlist(std::string_view term) const
Open a termlist returning synonyms for a term.
GlassPostListTable postlist_table
Table storing posting lists.
friend class GlassWritableDatabase
GlassSynonymTable synonym_table
Table storing synonym data.
bool open_tables(int flags)
Open all tables at most recent revision.
void throw_termlist_table_close_exception() const
friend class GlassTermList
Xapian::doccount get_value_freq(Xapian::valueno slot) const
Return the frequency of a given value slot.
friend class GlassSynonymTermList
Xapian::rev get_revision() const
Get the revision number which the tables are opened at.
bool has_positions() const
Check whether this database contains any positional information.
void replace_document_data(Xapian::docid did, const std::string &data)
Replace the document data for document did.
Definition: glass_docdata.h:87
bool delete_document_data(Xapian::docid did)
Delete the document data for document did.
void readahead_for_document(Xapian::docid did) const
A document read from a GlassDatabase.
Xapian::termcount positionlist_count(std::string_view data) const
Return the number of entries in specified position list data.
GlassPositionList * open_position_list(Xapian::docid did, std::string_view term)
A position list in a glass database.
void open(int flags_, const RootInfo &root_info, glass_revision_number_t rev)
bool document_exists(Xapian::docid did, Xapian::Internal::intrusive_ptr< const GlassDatabase > db) const
Check if document did exists.
void get_used_docid_range(Xapian::docid &first, Xapian::docid &last) const
static std::string make_key(std::string_view term, Xapian::docid did)
Compose a key from a termname and docid.
Xapian::termcount get_doclength(Xapian::docid did, Xapian::Internal::intrusive_ptr< const GlassDatabase > db) const
Returns the length of document did.
bool term_exists(std::string_view term) const
void get_freqs(std::string_view term, Xapian::doccount *termfreq_ptr, Xapian::termcount *collfreq_ptr, Xapian::termcount *wdfub_ptr=NULL) const
Returns frequencies for a term.
A reusable position list in a glass database.
void assign_data(std::string &&data)
Fill list with data, and move the position to the start.
void read_data(Xapian::docid did, std::string_view term)
Fill list with data, and move the position to the start.
void cancel(const RootInfo &root_info, glass_revision_number_t rev)
Override methods of GlassTable.
Xapian::termcount remove_word(std::string_view word, Xapian::termcount freqdec)
bool is_modified() const
Override methods of GlassTable.
void merge_changes()
Merge in batched-up changes.
TermList * open_termlist(std::string_view word)
Xapian::termcount flush_db()
Returns updated wordfreq upper bound.
void add_word(std::string_view word, Xapian::termcount freqinc)
Xapian::doccount get_word_frequency(std::string_view word) const
void set_wordfreq_upper_bound(Xapian::termcount ub)
void clear_synonyms(std::string_view term)
Remove all synonyms for term.
void flush_db()
Override methods of GlassTable.
void add_synonym(std::string_view term, std::string_view synonym)
Add a synonym for term.
TermList * open_termlist(std::string_view term)
Open synonym termlist for a term.
void remove_synonym(std::string_view term, std::string_view synonym)
Remove a synonym for term.
bool is_modified() const
Override methods of GlassTable.
void cancel(const RootInfo &root_info, glass_revision_number_t rev)
Override methods of GlassTable.
void create_and_open(int flags_, const RootInfo &root_info)
Create a new empty btree structure on disk and open it at the initial revision.
bool sync()
Definition: glass_table.h:542
void commit(glass_revision_number_t revision, RootInfo *root_info)
Commit any outstanding changes to the table.
bool exists() const
Determine whether the btree exists on disk.
bool is_open() const
Return true if this table is open.
Definition: glass_table.h:513
void flush_db()
Flush any outstanding changes to the DB file of the table.
bool empty() const
Return true if there are no entries in the table.
Definition: glass_table.h:690
void open(int flags_, const RootInfo &root_info, glass_revision_number_t rev)
Open the btree.
bool is_modified() const
Determine whether the object contains uncommitted modifications.
Definition: glass_table.h:706
void set_changes(GlassChanges *changes)
Set the GlassChanges object to write changed blocks to.
Definition: glass_table.h:730
GlassCursor * cursor_get() const
Get a cursor for reading from the table.
bool get_exact_entry(std::string_view key, std::string &tag) const
Read an entry from the table, if and only if it is exactly that being asked for.
void close(bool permanent=false)
Close the Btree.
bool del(std::string_view key)
Delete an entry from the table.
void cancel(const RootInfo &root_info, glass_revision_number_t rev)
Cancel any outstanding changes.
void set_flags(int new_flags)
Definition: glass_table.h:635
static void throw_database_closed()
Throw an exception indicating that the database is closed.
int get_flags() const
Definition: glass_table.h:633
void add(std::string_view key, std::string_view tag, bool already_compressed=false)
Add a key/tag pair to the table, replacing any existing pair with the same key.
bool readahead_key(std::string_view key) const
void set_termlist(Xapian::docid did, const Xapian::Document &doc, Xapian::termcount doclen)
Set the termlist data for document did.
void delete_termlist(Xapian::docid did)
Delete the termlist data for document did.
A TermList in a glass database.
Xapian::termcount get_doclength() const
Return the length of this document.
TermList * next()
Advance the current position to the next term in the termlist.
Xapian::termcount get_approx_size() const
Return approximate size of this termlist.
Xapian::termcount get_wdf() const
Return the wdf for the term at the current position.
bool not_present() const
Check if the term isn't present.
Glass class for value streams.
void add_document(Xapian::docid did, const Xapian::Document &doc, std::map< Xapian::valueno, ValueStats > &value_stats)
void set_value_stats(std::map< Xapian::valueno, ValueStats > &value_stats)
Write the updated statistics to the table.
Xapian::doccount get_value_freq(Xapian::valueno slot) const
Definition: glass_values.h:137
void replace_document(Xapian::docid did, const Xapian::Document &doc, std::map< Xapian::valueno, ValueStats > &value_stats)
std::string get_value_upper_bound(Xapian::valueno slot) const
Definition: glass_values.h:147
bool is_modified() const
Definition: glass_values.h:166
std::string get_value_lower_bound(Xapian::valueno slot) const
Definition: glass_values.h:142
void delete_document(Xapian::docid did, std::map< Xapian::valueno, ValueStats > &value_stats)
The GlassVersion class manages the revision files.
Definition: glass_version.h:96
RootInfo * root_to_set(Glass::table_type tbl)
void set_oldest_changeset(glass_revision_number_t changeset) const
const RootInfo & get_root(Glass::table_type tbl) const
void check_wdf(Xapian::termcount wdf)
void add_document(Xapian::termcount doclen)
const std::string write(glass_revision_number_t new_rev, int flags)
Xapian::docid get_last_docid() const
glass_revision_number_t get_revision() const
void delete_document(Xapian::termcount doclen)
Xapian::termcount get_doclength_lower_bound() const
std::string get_uuid_string() const
Return UUID in the standard 36 character string format.
void set_spelling_wordfreq_upper_bound(Xapian::termcount ub)
void set_last_docid(Xapian::docid did)
glass_revision_number_t get_oldest_changeset() const
Xapian::termcount get_spelling_wordfreq_upper_bound() const
bool sync(const std::string &tmpfile, glass_revision_number_t new_rev, int flags)
Xapian::doccount get_doccount() const
void set_changes(GlassChanges *changes_)
Xapian::termcount get_wdf_upper_bound() const
Xapian::docid get_next_docid()
void create(unsigned blocksize)
Create the version file.
void read()
Read the version file and check it's a version we understand.
Xapian::totallength get_total_doclen() const
Xapian::termcount get_unique_terms_lower_bound() const
Xapian::termcount get_doclength_upper_bound() const
void flush_postlist_changes()
Flush any unflushed postlist changes, but don't commit them.
Xapian::docid add_document_(Xapian::docid did, const Xapian::Document &document)
bool term_exists(std::string_view term) const
void remove_synonym(std::string_view word, std::string_view synonym) const
Remove a synonym for a term.
Xapian::termcount get_unique_terms(Xapian::docid did) const
Get the number of unique terms in document.
TermList * open_synonym_keylist(std::string_view prefix) const
Open a termlist returning each term which has synonyms.
std::map< Xapian::valueno, ValueStats > value_stats
std::string get_value_upper_bound(Xapian::valueno slot) const
Get an upper bound on the values stored in the given value slot.
TermList * open_spelling_wordlist() const
Return a termlist which returns the words which are spelling correction targets.
void get_freqs(std::string_view term, Xapian::doccount *termfreq_ptr, Xapian::termcount *collfreq_ptr) const
Returns frequencies for a term.
void close()
Close all the tables permanently.
bool has_uncommitted_changes() const
Return true if there are uncommitted changes.
ValueList * open_value_list(Xapian::valueno slot) const
Open a value stream.
void commit()
Implementation of virtual methods: see Database::Internal for details.
void replace_document(Xapian::docid did, const Xapian::Document &document)
void delete_document(Xapian::docid did)
void invalidate_doc_object(Xapian::Document::Internal *obj) const
Notify the database that document is no longer valid.
void add_synonym(std::string_view word, std::string_view synonym) const
Add a synonym for a term.
void apply()
Apply changes.
void cancel()
Cancel pending modifications to the database.
LeafPostList * open_leaf_post_list(std::string_view term, bool need_read_pos) const
Create a LeafPostList for use during a match.
void read_position_list(GlassRePositionList *pos_list, Xapian::docid did, std::string_view term) const
PostList * open_post_list(std::string_view term) const
Return a PostList suitable for use in a PostingIterator.
std::string get_value_lower_bound(Xapian::valueno slot) const
Get a lower bound on the values stored in the given value slot.
Xapian::Document::Internal * modify_shortcut_document
A pointer to the last document which was returned by open_document(), or NULL if there is no such val...
void add_spelling(std::string_view word, Xapian::termcount freqinc) const
Add a word to the spelling dictionary.
Xapian::Database::Internal * update_lock(int flags)
Lock a read-only database for writing or unlock a writable database.
Xapian::termcount get_doclength(Xapian::docid did) const
Virtual methods of Database::Internal.
void check_flush_threshold()
Check if we should autoflush.
TermList * open_allterms(std::string_view prefix) const
Xapian::doccount flush_threshold
If change_count reaches this threshold we automatically flush.
Xapian::docid add_document(const Xapian::Document &document)
Xapian::docid modify_shortcut_docid
The document ID for the last document returned by open_document().
Xapian::termcount positionlist_count(Xapian::docid did, std::string_view term) const
PositionList * open_position_list(Xapian::docid did, std::string_view term) const
Xapian::Document::Internal * open_document(Xapian::docid did, bool lazy) const
Open a handle on a document.
bool has_positions() const
Check whether this database contains any positional information.
void set_metadata(std::string_view key, std::string_view value)
Set the metadata associated with a given key.
Xapian::doccount get_value_freq(Xapian::valueno slot) const
Return the frequency of a given value slot.
Xapian::termcount remove_spelling(std::string_view word, Xapian::termcount freqdec) const
Remove a word from the spelling dictionary.
Xapian::doccount change_count
The number of documents added, deleted, or replaced since the last flush.
void clear_synonyms(std::string_view word) const
Clear all synonyms for a term.
bool get_deltas(std::string_view term, Xapian::termcount &tf_delta, Xapian::termcount &cf_delta) const
void delete_doclength(Xapian::docid did)
void flush_post_list(GlassPostListTable &table, std::string_view term)
Flush postlist changes for term.
void remove_posting(Xapian::docid did, const std::string &term, Xapian::doccount wdf)
void update_posting(Xapian::docid did, const std::string &term, Xapian::termcount old_wdf, Xapian::termcount new_wdf)
bool get_positionlist(Xapian::docid did, std::string_view term, std::string &s) const
bool has_positions(const GlassPositionListTable &position_table) const
void delete_positionlist(Xapian::docid did, std::string_view term)
void set_positionlist(Xapian::docid did, std::string_view term, std::string_view s)
bool get_doclength(Xapian::docid did, Xapian::termcount &doclen) const
void clear()
void set_doclength(Xapian::docid did, Xapian::termcount doclen, bool add)
void flush_doclengths(GlassPostListTable &table)
Flush document length changes.
void flush_post_lists(GlassPostListTable &table, std::string_view pfx)
Flush postlist changes for all terms which start with pfx.
void add_posting(Xapian::docid did, const std::string &term, Xapian::doccount wdf)
void flush_pos_lists(GlassPositionListTable &table)
Flush position changes.
void flush(GlassPostListTable &table)
Flush all postlist table changes.
Abstract base class for leaf postlists.
Definition: leafpostlist.h:40
A RemoteConnection object provides a bidirectional connection to another RemoteConnection object on a...
void send_message(char type, std::string_view s, double end_time)
Send a message.
void send_file(char type, int fd, double end_time)
Send the contents of a file as a message.
DatabaseCreateError indicates a failure to create a database.
Definition: error.h:439
DatabaseError indicates some sort of database related error.
Definition: error.h:355
Indicates an attempt to access a database not present.
Definition: error.h:1043
Virtual base class for Database internals.
void dtor_called()
Helper to process uncommitted changes when a writable db is destroyed.
bool transaction_active() const
Test if a transaction is currently active.
An indexed database of documents.
Definition: database.h:75
Abstract base class for a document.
Class representing a document.
Definition: document.h:64
std::string get_data() const
Get the document data.
Definition: document.cc:75
Xapian::Internal::intrusive_ptr_nonnull< Internal > internal
Definition: document.h:67
TermIterator termlist_end() const noexcept
End iterator corresponding to termlist_begin().
Definition: document.h:219
TermIterator termlist_begin() const
Start iterating the terms in this document.
Definition: document.cc:179
All exceptions thrown by Xapian are subclasses of Xapian::Error.
Definition: error.h:41
const std::string & get_msg() const noexcept
Message giving details of the error, intended for human consumption.
Definition: error.h:111
std::string get_description() const
Return a string describing this object.
Definition: error.cc:93
Indicates an attempt to use a feature which is unavailable.
Definition: error.h:707
Abstract base class for postlists.
Definition: postlist.h:40
A smart pointer that uses intrusive reference counting.
Definition: intrusive_ptr.h:83
InvalidArgumentError indicates an invalid parameter value was passed to the API.
Definition: error.h:229
InvalidOperationError indicates the API was used in an invalid way.
Definition: error.h:271
Abstract base class for iterating term positions in a document.
Definition: positionlist.h:32
Class representing a query.
Definition: query.h:45
const TermIterator get_unique_terms_begin() const
Begin iterator for unique terms in the query object.
Definition: query.cc:223
Abstract base class for termlists.
Definition: termlist.h:42
const std::string & get_termname() const
Return the termname at the current position.
Definition: termlist.h:69
Class for iterating over a list of terms.
Definition: termiterator.h:41
Abstract base class for value streams.
Definition: valuelist.h:31
#define UNSIGNED_OVERFLOW_OK(X)
Definition: config.h:626
#define rare(COND)
Definition: config.h:607
Constants in the Xapian namespace.
Iterate all document ids when they form a contiguous range.
string term
PositionList * p
Debug logging macros.
#define RETURN(...)
Definition: debuglog.h:484
#define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS)
Definition: debuglog.h:478
#define LOGLINE(a, b)
Definition: debuglog.h:485
#define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS)
Definition: debuglog.h:480
#define LOGCALL_VOID(CATEGORY, FUNC, PARAMS)
Definition: debuglog.h:479
#define LOGCALL_DTOR(CATEGORY, CLASS)
Definition: debuglog.h:481
Hierarchy of classes which Xapian can throw as exceptions.
Wrapper class around a file descriptor to avoid leaks.
Utility functions for testing files.
bool dir_exists(const char *path)
Test if a directory exists.
Definition: filetests.h:145
A PostList which iterates over all documents in a GlassDatabase.
A termlist containing all terms in a glass database.
#define MAX_SAFE_TERM_LENGTH
C++ class definition for glass database.
Definitions, types, etc for use inside glass.
#define GLASS_MIN_BLOCKSIZE
Minimum B-tree block size.
Definition: glass_defs.h:33
#define GLASS_MAX_DOCID
The largest docid value supported by glass.
Definition: glass_defs.h:50
#define GLASS_DEFAULT_BLOCKSIZE
Default B-tree block size.
Definition: glass_defs.h:30
uint4 glass_revision_number_t
The revision number of a glass database.
Definition: glass_defs.h:68
#define GLASS_TABLE_EXTENSION
Glass table extension.
Definition: glass_defs.h:27
#define GLASS_MAX_BLOCKSIZE
Maximum B-tree block size.
Definition: glass_defs.h:36
Subclass of GlassTable which holds document data.
A document read from a GlassDatabase.
Access to metadata for a glass database.
A position list in a glass database.
Postlists in glass databases.
Internal definitions for glass database replication.
#define CHANGES_MAGIC_STRING
#define REASONABLE_CHANGESET_SIZE
#define CHANGES_VERSION
A termlist containing all words which are spelling targets.
A TermList in a glass database.
Glass class for value streams.
GlassValueManager class.
#define true
Definition: header.h:8
size_t io_read(int fd, char *p, size_t n, size_t min)
Read n bytes (or until EOF) into block pointed to by p from file descriptor fd.
Definition: io_utils.cc:241
Wrappers for low-level POSIX I/O routines.
@ DOCDATA
Definition: glass_defs.h:55
@ SYNONYM
Definition: glass_defs.h:59
@ POSITION
Definition: glass_defs.h:57
@ POSTLIST
Definition: glass_defs.h:54
@ TERMLIST
Definition: glass_defs.h:56
@ SPELLING
Definition: glass_defs.h:58
double end_time(double timeout)
Return the end time for a timeout in timeout seconds.
Definition: realtime.h:95
string str(int value)
Convert int to std::string.
Definition: str.cc:91
The Xapian namespace contains public interfaces for the Xapian library.
Definition: compactor.cc:82
const int DB_CREATE
Create a new database.
Definition: constants.h:43
int revision()
Report the revision of the library which the program is linked with.
Definition: xapian.h:146
const int DB_RETRY_LOCK
If the database is already locked, retry the lock.
Definition: constants.h:144
unsigned XAPIAN_TERMCOUNT_BASE_TYPE termcount
A counts of terms.
Definition: types.h:64
const int DB_OPEN
Open an existing database.
Definition: constants.h:49
XAPIAN_REVISION_TYPE rev
Revision number of a database.
Definition: types.h:108
unsigned valueno
The number for a value slot in a document.
Definition: types.h:90
unsigned XAPIAN_DOCID_BASE_TYPE doccount
A count of documents.
Definition: types.h:37
const int DB_NO_TERMLIST
When creating a database, don't create a termlist table.
Definition: constants.h:135
unsigned XAPIAN_DOCID_BASE_TYPE docid
A unique identifier for a document.
Definition: types.h:51
XAPIAN_TOTALLENGTH_TYPE totallength
The total length of all documents in a database.
Definition: types.h:114
const int DB_CREATE_OR_OVERWRITE
Create database if it doesn't already exist, or overwrite if it does.
Definition: constants.h:37
#define Assert(COND)
Definition: omassert.h:122
Pack types into strings and unpack them again.
bool unpack_uint(const char **p, const char *end, U *result)
Decode an unsigned integer from a string.
Definition: pack.h:346
void pack_uint(std::string &s, U value)
Append an encoded unsigned integer to a string.
Definition: pack.h:315
void pack_string(std::string &s, std::string_view value)
Append an encoded std::string to a string.
Definition: pack.h:442
Parse signed and unsigned type from string and check for trailing characters.
bool parse_unsigned(const char *p, T &res)
Definition: parseint.h:29
Provides wrappers with POSIXy semantics.
#define posixy_open
RemoteConnection class used by the remote backend.
Replication support for Xapian databases.
Replication protocol version and message numbers.
#define MAX_DB_COPIES_PER_CONVERSATION
@ REPL_REPLY_FAIL
@ REPL_REPLY_DB_FOOTER
@ REPL_REPLY_CHANGESET
@ REPL_REPLY_DB_FILEDATA
@ REPL_REPLY_DB_HEADER
@ REPL_REPLY_DB_FILENAME
@ REPL_REPLY_END_OF_CHANGES
#define O_CLOEXEC
Definition: safefcntl.h:89
include <sys/stat.h> with portability enhancements
Convert types to std::string.
Various handy string-related helpers.
#define CONST_STRLEN(S)
Returns the length of a string constant.
Definition: stringutils.h:48
#define STRINGIZE(X)
The STRINGIZE macro converts its parameter into a string constant.
Definition: stringutils.h:41
Information about the steps involved in performing a replication.
Definition: replication.h:32
bool changed
True if and only if the replication corresponds to a change in the live version of the database.
Definition: replication.h:44
int fullcopy_count
Number of times a full database copy was performed.
Definition: replication.h:37
int changeset_count
Number of changesets applied.
Definition: replication.h:34
Class for iterating over document values.
Statistics about values.