xapian-core  2.0.0
glass_table.cc
Go to the documentation of this file.
1 
4 /* Copyright 1999,2000,2001 BrightStation PLC
5  * Copyright 2002 Ananova Ltd
6  * Copyright 2002-2025 Olly Betts
7  * Copyright 2008 Lemur Consulting Ltd
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, see
21  * <https://www.gnu.org/licenses/>.
22  */
23 
24 #include <config.h>
25 
26 #include "glass_table.h"
27 
28 #include <xapian/error.h>
29 
30 #include "omassert.h"
31 #include "posixy_wrapper.h"
32 #include "str.h"
33 #include "stringutils.h" // For STRINGIZE().
34 
35 #include <sys/types.h>
36 
37 #include <cerrno>
38 #include <cstring> /* for memmove */
39 #include <climits> /* for CHAR_BIT */
40 
41 #include "glass_freelist.h"
42 #include "glass_changes.h"
43 #include "glass_cursor.h"
44 #include "glass_defs.h"
45 #include "glass_version.h"
46 
47 #include "debuglog.h"
48 #include "filetests.h"
49 #include "io_utils.h"
50 #include "pack.h"
51 #include "wordaccess.h"
52 
53 #include <algorithm> // for std::min()
54 #include <string>
55 #include <string_view>
56 
57 #include "xapian/constants.h"
58 
59 using namespace Glass;
60 using namespace std;
61 
62 [[noreturn]]
63 static void
64 throw_corrupt(const char* message)
65 {
66  throw Xapian::DatabaseCorruptError(message);
67 }
68 
69 //#define BTREE_DEBUG_FULL 1
70 #undef BTREE_DEBUG_FULL
71 
72 #ifdef BTREE_DEBUG_FULL
73 /*------debugging aids from here--------*/
74 
75 static void print_key(const uint8_t * p, int c, int j);
76 static void print_tag(const uint8_t * p, int c, int j);
77 
78 /*
79 static void report_cursor(int N, Btree * B, Glass::Cursor * C)
80 {
81  int i;
82  printf("%d)\n", N);
83  for (i = 0; i <= B->level; i++)
84  printf("p=%d, c=%d, n=[%d], rewrite=%d\n",
85  C[i].p, C[i].c, C[i].n, C[i].rewrite);
86 }
87 */
88 
89 /*------to here--------*/
90 #endif /* BTREE_DEBUG_FULL */
91 
92 static inline uint8_t *zeroed_new(size_t size)
93 {
94  uint8_t *temp = new uint8_t[size];
95  memset(temp, 0, size);
96  return temp;
97 }
98 
99 /* A B-tree consists of a file with extension GLASS_TABLE_EXTENSION divided
100  into a sequence of equal sized blocks, numbered 0, 1, 2 ... some of which
101  are free, some in use. Those in use are arranged in a tree. Lists of
102  currently unused blocks are stored in a freelist which is itself stored in
103  unused blocks.
104 
105  Some "root info" is needed to locate a particular revision of the B-tree
106  - this is stored in the "version file" (we store this data for all tables
107  at a particular revision together).
108 
109  Each block, b, has a structure like this:
110 
111  R L M T D o1 o2 o3 ... oN <gap> [item] .. [item] .. [item] ...
112  <---------- D ----------> <-M->
113 
114  And then,
115 
116  R = REVISION(b) is the revision number the B-tree had when the block was
117  written into the DB file.
118  L = GET_LEVEL(b) is the level of the block, which is the number of levels
119  towards the root of the B-tree structure. So leaf blocks
120  have level 0 and the one root block has the highest level
121  equal to the number of levels in the B-tree. For blocks
122  storing the freelist, the level is set to 254.
123  M = MAX_FREE(b) is the size of the gap between the end of the directory and
124  the first item of data. (It is not necessarily the maximum
125  size among the bits of space that are free, but I can't
126  think of a better name.)
127  T = TOTAL_FREE(b)is the total amount of free space left in b.
128  D = DIR_END(b) gives the offset to the end of the directory.
129 
130  o1, o2 ... oN are a directory of offsets to the N items held in the block.
131  The items are key-tag pairs, and as they occur in the directory are ordered
132  by the keys.
133 
134  An item has this form:
135 
136  I K key X tag
137  ←K→
138  <------I---->
139 
140  A long tag presented through the API is split up into C pieces small enough
141  to be accommodated in the blocks of the B-tree. The key is extended to
142  include a counter, x, which runs from 1 to C. The key is preceded by a
143  length, K, and the whole item with a length, I, as depicted above. The
144  upper bits of I encode a flag indicating if this item is compressed, and a
145  flag saying if this is the last piece of a tag (i.e. if x == C).
146 
147  Here are the corresponding definitions:
148 
149 */
150 
153 #define SEQ_START_POINT (-10)
154 
155 /* Note use of the limits.h values:
156  UCHAR_MAX = 255, an unsigned with all bits set, and
157  CHAR_BIT = 8, the number of bits per byte
158 
159  BYTE_PAIR_RANGE below is the smallest +ve number that can't be held in two
160  bytes -- 64K effectively.
161 */
162 
163 #define BYTE_PAIR_RANGE (1 << 2 * CHAR_BIT)
164 
166 void
167 GlassTable::read_block(uint4 n, uint8_t * p) const
168 {
169  // Log the value of p, not the contents of the block it points to...
170  LOGCALL_VOID(DB, "GlassTable::read_block", n | (void*)p);
171  if (rare(handle == -2))
173  AssertRel(n,<,free_list.get_first_unused_block());
174 
175  io_read_block(handle, reinterpret_cast<char *>(p), block_size, n, offset);
176 
177  if (GET_LEVEL(p) != LEVEL_FREELIST) {
178  int dir_end = DIR_END(p);
179  if (rare(dir_end < DIR_START || unsigned(dir_end) > block_size)) {
180  string msg("dir_end invalid in block ");
181  msg += str(n);
182  throw Xapian::DatabaseCorruptError(msg);
183  }
184  }
185 }
186 
197 void
198 GlassTable::write_block(uint4 n, const uint8_t * p, bool appending) const
199 {
200  LOGCALL_VOID(DB, "GlassTable::write_block", n | p | appending);
201  Assert(writable);
202  /* Check that n is in range. */
203  AssertRel(n,<,free_list.get_first_unused_block());
204 
205  /* don't write to non-free */
206  // FIXME: We can no longer check this easily.
207  // AssertParanoid(free_list.block_free_at_start(block_size, n));
208 
209  /* write revision is okay */
210  AssertEqParanoid(REVISION(p), revision_number + 1);
211 
212  if (appending) {
213  // In the case of the freelist, new entries can safely be written into
214  // the space at the end of the latest freelist block, as that's unused
215  // by previous revisions. It is also safe to write into a block which
216  // wasn't used in the previous revision.
217  //
218  // It's only when we start a new freelist block that we need to worry
219  // about invalidating old revisions.
220  } else if (flags & Xapian::DB_DANGEROUS) {
221  // FIXME: We should somehow flag this to prevent readers opening the
222  // database. Removing "iamglass" or setting a flag in it doesn't deal
223  // with any existing readers though. Perhaps we want to have readers
224  // read lock and try to take an exclusive lock here?
225  }
226 
227  const char * p_char = reinterpret_cast<const char *>(p);
228  io_write_block(handle, p_char, block_size, n, offset);
229 
230  if (!changes_obj) return;
231 
232  unsigned char v;
233  // FIXME: track table_type in this class?
234  if (strcmp(tablename, "position") == 0) {
235  v = int(Glass::POSITION);
236  } else if (strcmp(tablename, "postlist") == 0) {
237  v = int(Glass::POSTLIST);
238  } else if (strcmp(tablename, "docdata") == 0) {
239  v = int(Glass::DOCDATA);
240  } else if (strcmp(tablename, "spelling") == 0) {
241  v = int(Glass::SPELLING);
242  } else if (strcmp(tablename, "synonym") == 0) {
243  v = int(Glass::SYNONYM);
244  } else if (strcmp(tablename, "termlist") == 0) {
245  v = int(Glass::TERMLIST);
246  } else {
247  return; // FIXME
248  }
249 
250  if (block_size == 2048) {
251  v |= 0 << 3;
252  } else if (block_size == 4096) {
253  v |= 1 << 3;
254  } else if (block_size == 8192) {
255  v |= 2 << 3;
256  } else if (block_size == 16384) {
257  v |= 3 << 3;
258  } else if (block_size == 32768) {
259  v |= 4 << 3;
260  } else if (block_size == 65536) {
261  v |= 5 << 3;
262  } else {
263  return; // FIXME
264  }
265 
266  string buf;
267  buf += char(v);
268  // Write the block number to the file
269  pack_uint(buf, n);
270 
271  changes_obj->write_block(buf);
272  changes_obj->write_block(reinterpret_cast<const char *>(p), block_size);
273 }
274 
275 /* A note on cursors:
276 
277  Each B-tree level has a corresponding array element C[j] in a
278  cursor, C. C[0] is the leaf (or data) level, and C[B->level] is the
279  root block level. Within a level j,
280 
281  C[j].p addresses the block
282  C[j].c is the offset into the directory entry in the block
283  C[j].n is the number of the block at C[j].p
284 
285  A look up in the B-tree causes navigation of the blocks starting
286  from the root. In each block, p, we find an offset, c, to an item
287  which gives the number, n, of the block for the next level. This
288  leads to an array of values p,c,n which are held inside the cursor.
289 
290  Any Btree object B has a built-in cursor, at B->C. But other cursors may
291  be created. If BC is a created cursor, BC->C is the cursor in the
292  sense given above, and BC->B is the handle for the B-tree again.
293 */
294 
295 void
297 {
298  LOGCALL_VOID(DB, "GlassTable::throw_overwritten", NO_ARGS);
299  // If we're writable, there shouldn't be another writer who could cause
300  // overwritten to be flagged, so that's a DatabaseCorruptError.
301  if (writable)
302  throw Xapian::DatabaseCorruptError("Block overwritten - run xapian-check on this database");
303  throw Xapian::DatabaseModifiedError("The revision being read has been discarded - you should call Xapian::Database::reopen() and retry the operation");
304 }
305 
306 /* block_to_cursor(C, j, n) puts block n into position C[j] of cursor
307  C, writing the block currently at C[j] back to disk if necessary.
308  Note that
309 
310  C[j].rewrite
311 
312  is true iff C[j].n is different from block n in file DB. If it is
313  false no rewriting is necessary.
314 */
315 
316 void
318 {
319  LOGCALL_VOID(DB, "GlassTable::block_to_cursor", (void*)C_ | j | n);
320  if (n == C_[j].get_n()) return;
321 
322  if (writable && C_[j].rewrite) {
323  Assert(C == C_);
324  write_block(C_[j].get_n(), C_[j].get_p());
325  C_[j].rewrite = false;
326  }
327 
328  // Check if the block is in the built-in cursor (potentially in
329  // modified form).
330  const uint8_t * p;
331  if (n == C[j].get_n()) {
332  p = C_[j].clone(C[j]);
333  } else {
334  uint8_t * q = C_[j].init(block_size);
335  read_block(n, q);
336  p = q;
337  C_[j].set_n(n);
338  }
339 
340  if (j < level) {
341  /* unsigned comparison */
342  if (rare(REVISION(p) > REVISION(C_[j + 1].get_p()))) {
343  throw_overwritten();
344  }
345  }
346 
347  if (rare(j != GET_LEVEL(p))) {
348  string msg = "Expected block ";
349  msg += str(n);
350  msg += " to be level ";
351  msg += str(j);
352  msg += ", not ";
353  msg += str(GET_LEVEL(p));
354  throw Xapian::DatabaseCorruptError(msg);
355  }
356 }
357 
379 void
381 {
382  LOGCALL_VOID(DB, "GlassTable::alter", NO_ARGS);
383  Assert(writable);
384  if (flags & Xapian::DB_DANGEROUS) {
385  C[0].rewrite = true;
386  return;
387  }
388  int j = 0;
389  while (true) {
390  if (C[j].rewrite) return; /* all new, so return */
391  C[j].rewrite = true;
392 
393  glass_revision_number_t rev = REVISION(C[j].get_p());
394  if (rev == revision_number + 1) {
395  return;
396  }
397  Assert(rev < revision_number + 1);
398  uint4 n = C[j].get_n();
399  free_list.mark_block_unused(this, block_size, n);
400  SET_REVISION(C[j].get_modifiable_p(block_size), revision_number + 1);
401  n = free_list.get_block(this, block_size);
402  C[j].set_n(n);
403 
404  if (j == level) return;
405  j++;
406  BItem_wr(C[j].get_modifiable_p(block_size), C[j].c).set_block_given_by(n);
407  }
408 }
409 
423 int
424 GlassTable::find_in_leaf(const uint8_t * p, LeafItem item, int c, bool& exact)
425 {
426  LOGCALL_STATIC(DB, int, "GlassTable::find_in_leaf", (const void*)p | (const void *)item.get_address() | c | Literal("bool&"));
427  // c should be odd (either -1, or an even offset from DIR_START).
428  Assert((c & 1) == 1);
429  int i = DIR_START;
430  i -= D2;
431  if (c != -1) {
432  AssertRel(i,<=,c);
433  }
434  int j = DIR_END(p);
435 
436  if (c != -1) {
437  if (c < j && i < c) {
438  int r = compare(LeafItem(p, c), item);
439  if (r == 0) {
440  exact = true;
441  return c;
442  }
443  if (r < 0) i = c;
444  }
445  c += D2;
446  if (c < j && i < c) {
447  int r = compare(item, LeafItem(p, c));
448  if (r == 0) {
449  exact = true;
450  return c;
451  }
452  if (r < 0) j = c;
453  }
454  }
455 
456  while (j - i > D2) {
457  int k = i + ((j - i) / (D2 * 2)) * D2; /* mid way */
458  int r = compare(item, LeafItem(p, k));
459  if (r < 0) {
460  j = k;
461  } else {
462  i = k;
463  if (r == 0) {
464  exact = true;
465  break;
466  }
467  }
468  }
469  AssertRel(DIR_START - D2,<=,i);
470  AssertRel(i,<,DIR_END(p));
471  RETURN(i);
472 }
473 
474 template<typename ITEM> int
475 find_in_branch_(const uint8_t * p, ITEM item, int c)
476 {
477  // c should be odd (either -1, or an even offset from DIR_START).
478  Assert((c & 1) == 1);
479  int i = DIR_START;
480  if (c != -1) {
481  AssertRel(i,<=,c);
482  }
483  int j = DIR_END(p);
484 
485  if (c != -1) {
486  if (c < j && i < c) {
487  int r = compare(BItem(p, c), item);
488  if (r == 0) return c;
489  if (r < 0) i = c;
490  }
491  c += D2;
492  if (c < j && i < c) {
493  int r = compare(item, BItem(p, c));
494  if (r == 0) return c;
495  if (r < 0) j = c;
496  }
497  }
498 
499  while (j - i > D2) {
500  int k = i + ((j - i) / (D2 * 2)) * D2; /* mid way */
501  int r = compare(item, BItem(p, k));
502  if (r < 0) {
503  j = k;
504  } else {
505  i = k;
506  if (r == 0) break;
507  }
508  }
509  AssertRel(DIR_START,<=,i);
510  AssertRel(i,<,DIR_END(p));
511  return i;
512 }
513 
514 int
515 GlassTable::find_in_branch(const uint8_t * p, LeafItem item, int c)
516 {
517  LOGCALL_STATIC(DB, int, "GlassTable::find_in_branch", (const void*)p | (const void *)item.get_address() | c);
518  RETURN(find_in_branch_(p, item, c));
519 }
520 
521 int
522 GlassTable::find_in_branch(const uint8_t * p, BItem item, int c)
523 {
524  LOGCALL_STATIC(DB, int, "GlassTable::find_in_branch", (const void*)p | (const void *)item.get_address() | c);
525  RETURN(find_in_branch_(p, item, c));
526 }
527 
535 bool
537 {
538  LOGCALL(DB, bool, "GlassTable::find", (void*)C_);
539  // Note: the parameter is needed when we're called by GlassCursor
540  const uint8_t * p;
541  int c;
542  for (int j = level; j > 0; --j) {
543  p = C_[j].get_p();
544  c = find_in_branch(p, kt, C_[j].c);
545 #ifdef BTREE_DEBUG_FULL
546  printf("Block in GlassTable:find - code position 1");
547  report_block_full(j, C_[j].get_n(), p);
548 #endif /* BTREE_DEBUG_FULL */
549  C_[j].c = c;
550  block_to_cursor(C_, j - 1, BItem(p, c).block_given_by());
551  }
552  p = C_[0].get_p();
553  bool exact = false;
554  c = find_in_leaf(p, kt, C_[0].c, exact);
555 #ifdef BTREE_DEBUG_FULL
556  printf("Block in GlassTable:find - code position 2");
557  report_block_full(0, C_[0].get_n(), p);
558 #endif /* BTREE_DEBUG_FULL */
559  C_[0].c = c;
560  RETURN(exact);
561 }
562 
568 void
570 {
571  LOGCALL_VOID(DB, "GlassTable::compact", (void*)p);
572  Assert(p != buffer);
573  Assert(writable);
574  int e = block_size;
575  uint8_t * b = buffer;
576  int dir_end = DIR_END(p);
577  if (GET_LEVEL(p) == 0) {
578  // Leaf.
579  for (int c = DIR_START; c < dir_end; c += D2) {
580  LeafItem item(p, c);
581  int l = item.size();
582  e -= l;
583  if (e < 0) throw_corrupt("Leaf item size extends outside block");
584  memcpy(b + e, item.get_address(), l);
585  LeafItem_wr::setD(p, c, e); /* reform in b */
586  }
587  } else {
588  // Branch.
589  for (int c = DIR_START; c < dir_end; c += D2) {
590  BItem item(p, c);
591  int l = item.size();
592  e -= l;
593  if (e < 0) throw_corrupt("Branch item size extends outside block");
594  memcpy(b + e, item.get_address(), l);
595  BItem_wr::setD(p, c, e); /* reform in b */
596  }
597  }
598  memcpy(p + e, b + e, block_size - e); /* copy back */
599  e -= dir_end;
600  if (e < 0) throw_corrupt("Items overlap with item pointers");
601  SET_TOTAL_FREE(p, e);
602  SET_MAX_FREE(p, e);
603 }
604 
608 void
610 {
611  LOGCALL_VOID(DB, "GlassTable::split_root", split_n);
612  /* gain a level */
613  ++level;
614 
615  /* check level overflow - this isn't something that should ever happen
616  * but deserves more than an Assert()... */
617  if (level == GLASS_BTREE_CURSOR_LEVELS) {
618  throw Xapian::DatabaseCorruptError("Btree has grown impossibly large ("
620  " levels)");
621  }
622 
623  uint8_t * q = C[level].init(block_size);
624  memset(q, 0, block_size);
625  C[level].c = DIR_START;
626  C[level].set_n(free_list.get_block(this, block_size));
627  C[level].rewrite = true;
628  SET_REVISION(q, revision_number + 1);
629  SET_LEVEL(q, level);
631  compact(q); /* to reset TOTAL_FREE, MAX_FREE */
632 
633  /* form a null key in b with a pointer to the old root */
634  uint8_t b[10]; /* 7 is exact */
635  BItem_wr item(b);
636  item.form_null_key(split_n);
637  add_branch_item(item, level);
638 }
639 
655 void
657 {
658  LOGCALL_VOID(DB, "GlassTable::enter_key_above_leaf", Literal("previtem") | Literal("newitem"));
659  Assert(writable);
660  Assert(compare(previtem, newitem) < 0);
661 
662  Key prevkey = previtem.key();
663  Key newkey = newitem.key();
664  int new_comp = newitem.component_of();
665 
666  uint4 blocknumber = C[0].get_n();
667 
668  // FIXME update to use Key
669  // Keys are truncated here: but don't truncate the count at the end away.
670  const int newkey_len = newkey.length();
671  AssertRel(newkey_len,>,0);
672 
673  // Truncate the key to the minimal key which differs from prevkey,
674  // the preceding key in the block.
675  int i = 0;
676  const int min_len = min(newkey_len, prevkey.length());
677  while (i < min_len && prevkey[i] == newkey[i]) {
678  i++;
679  }
680 
681  // Want one byte of difference.
682  if (i < newkey_len) i++;
683 
684  // Enough space for a branch item with maximum length key.
685  uint8_t b[BYTES_PER_BLOCK_NUMBER + K1 + 255 + X2];
686  BItem_wr item(b);
687  AssertRel(i, <=, 255);
688  item.set_truncated_key_and_block(newkey, new_comp, i, blocknumber);
689 
690  // The split block gets inserted into the parent after the pointer to the
691  // current child.
692  AssertEq(C[1].c, find_in_branch(C[1].get_p(), item, C[1].c));
693  C[1].c += D2;
694  C[1].rewrite = true; /* a subtle point: this *is* required. */
695  add_branch_item(item, 1);
696 }
697 
711 void
713 {
714  LOGCALL_VOID(DB, "GlassTable::enter_key_above_branch", j | Literal("newitem"));
715  Assert(writable);
716  AssertRel(j,>,1);
717 
718  /* Can't truncate between branch levels, since the separated keys
719  * are in at the leaf level, and truncating again will change the
720  * branch point.
721  */
722 
723  uint4 blocknumber = C[j - 1].get_n();
724 
725  // Enough space for a branch item with maximum length key.
726  uint8_t b[BYTES_PER_BLOCK_NUMBER + K1 + 255 + X2];
727  BItem_wr item(b);
728  item.set_key_and_block(newitem.key(), blocknumber);
729 
730  // The split block gets inserted into the parent after the pointer to the
731  // current child.
732  AssertEq(C[j].c, find_in_branch(C[j].get_p(), item, C[j].c));
733  C[j].c += D2;
734  C[j].rewrite = true; /* a subtle point: this *is* required. */
735  add_branch_item(item, j);
736 }
737 
742 int
743 GlassTable::mid_point(uint8_t * p) const
744 {
745  LOGCALL(DB, int, "GlassTable::mid_point", (void*)p);
746  int n = 0;
747  int dir_end = DIR_END(p);
748  int size = block_size - TOTAL_FREE(p) - dir_end;
749  for (int c = DIR_START; c < dir_end; c += D2) {
750  int l;
751  if (GET_LEVEL(p) == 0) {
752  l = LeafItem(p, c).size();
753  } else {
754  l = BItem(p, c).size();
755  }
756  n += 2 * l;
757  if (n >= size) {
758  if (l < n - size) RETURN(c);
759  RETURN(c + D2);
760  }
761  }
762 
763  /* This shouldn't happen, as the sum of the item sizes should be the same
764  * as the value calculated in size, so assert but return a sane value just
765  * in case. */
766  Assert(false);
767  RETURN(dir_end);
768 }
769 
779 void
781 {
782  LOGCALL_VOID(DB, "GlassTable::add_item_to_leaf", (void*)p | Literal("kt_") | c);
783  Assert(writable);
784  int dir_end = DIR_END(p);
785  int kt_len = kt_.size();
786  int needed = kt_len + D2;
787  int new_total = TOTAL_FREE(p) - needed;
788  int new_max = MAX_FREE(p) - needed;
789 
790  Assert(new_total >= 0);
791 
792  AssertRel(MAX_FREE(p),>=,needed);
793 
794  AssertRel(DIR_START,<=,c);
795  AssertRel(c,<=,dir_end);
796 
797  memmove(p + c + D2, p + c, dir_end - c);
798  dir_end += D2;
799  SET_DIR_END(p, dir_end);
800 
801  int o = dir_end + new_max;
802  LeafItem_wr::setD(p, c, o);
803  memmove(p + o, kt_.get_address(), kt_len);
804 
805  SET_MAX_FREE(p, new_max);
806 
807  SET_TOTAL_FREE(p, new_total);
808 }
809 
819 void
820 GlassTable::add_item_to_branch(uint8_t * p, BItem kt_, int c)
821 {
822  LOGCALL_VOID(DB, "GlassTable::add_item_to_branch", (void*)p | Literal("kt_") | c);
823  Assert(writable);
824  int dir_end = DIR_END(p);
825  int kt_len = kt_.size();
826  int needed = kt_len + D2;
827  int new_total = TOTAL_FREE(p) - needed;
828  int new_max = MAX_FREE(p) - needed;
829 
830  Assert(new_total >= 0);
831 
832  AssertRel(MAX_FREE(p),>=,needed);
833 
834  AssertRel(DIR_START,<=,c);
835  AssertRel(c,<=,dir_end);
836 
837  memmove(p + c + D2, p + c, dir_end - c);
838  dir_end += D2;
839  SET_DIR_END(p, dir_end);
840 
841  int o = dir_end + new_max;
842  BItem_wr::setD(p, c, o);
843  memmove(p + o, kt_.get_address(), kt_len);
844 
845  SET_MAX_FREE(p, new_max);
846 
847  SET_TOTAL_FREE(p, new_total);
848 }
849 
855 void
857 {
858  LOGCALL_VOID(DB, "GlassTable::add_leaf_item", Literal("kt_"));
859  Assert(writable);
860  uint8_t * p = C[0].get_modifiable_p(block_size);
861  int c = C[0].c;
862  uint4 n;
863 
864  int needed = kt_.size() + D2;
865  if (TOTAL_FREE(p) < needed) {
866  int m;
867  // Prepare to split p. After splitting, the block is in two halves, the
868  // lower half is split_p, the upper half p again. add_to_upper_half
869  // becomes true when the item gets added to p, false when it gets added
870  // to split_p.
871 
872  if (seq_count < 0) {
873  // If we're not in sequential mode, we split at the mid point
874  // of the node.
875  m = mid_point(p);
876  } else {
877  // During sequential addition, split at the insert point
878  AssertRel(c,>=,DIR_START);
879  m = c;
880  }
881 
882  uint4 split_n = C[0].get_n();
883  C[0].set_n(free_list.get_block(this, block_size));
884 
885  memcpy(split_p, p, block_size); // replicate the whole block in split_p
886  SET_DIR_END(split_p, m);
887  compact(split_p); /* to reset TOTAL_FREE, MAX_FREE */
888 
889  {
890  int residue = DIR_END(p) - m;
891  int new_dir_end = DIR_START + residue;
892  memmove(p + DIR_START, p + m, residue);
893  SET_DIR_END(p, new_dir_end);
894  }
895 
896  compact(p); /* to reset TOTAL_FREE, MAX_FREE */
897 
898  bool add_to_upper_half;
899  if (seq_count < 0) {
900  add_to_upper_half = (c >= m);
901  } else {
902  // And add item to lower half if split_p has room, otherwise upper
903  // half
904  add_to_upper_half = (TOTAL_FREE(split_p) < needed);
905  }
906 
907  if (add_to_upper_half) {
908  c -= (m - DIR_START);
909  Assert(seq_count < 0 || c <= DIR_START + D2);
910  Assert(c >= DIR_START);
911  Assert(c <= DIR_END(p));
912  add_item_to_leaf(p, kt_, c);
913  n = C[0].get_n();
914  } else {
915  Assert(c >= DIR_START);
916  Assert(c <= DIR_END(split_p));
917  add_item_to_leaf(split_p, kt_, c);
918  n = split_n;
919  }
920  write_block(split_n, split_p);
921 
922  // Check if we're splitting the root block.
923  if (0 == level) split_root(split_n);
924 
925  /* Enter a separating key at level 1 between */
926  /* the last key of block split_p, and the first key of block p */
927  enter_key_above_leaf(LeafItem(split_p, DIR_END(split_p) - D2),
928  LeafItem(p, DIR_START));
929  } else {
930  AssertRel(TOTAL_FREE(p),>=,needed);
931 
932  if (MAX_FREE(p) < needed) {
933  compact(p);
934  AssertRel(MAX_FREE(p),>=,needed);
935  }
936 
937  add_item_to_leaf(p, kt_, c);
938  n = C[0].get_n();
939  }
940 
941  changed_n = n;
942  changed_c = c;
943 }
944 
950 void
952 {
953  LOGCALL_VOID(DB, "GlassTable::add_branch_item", Literal("kt_") | j);
954  Assert(writable);
955  uint8_t * p = C[j].get_modifiable_p(block_size);
956  int c = C[j].c;
957 
958  int needed = kt_.size() + D2;
959  if (TOTAL_FREE(p) < needed) {
960  int m;
961  // Prepare to split p. After splitting, the block is in two halves, the
962  // lower half is split_p, the upper half p again. add_to_upper_half
963  // becomes true when the item gets added to p, false when it gets added
964  // to split_p.
965 
966  if (seq_count < 0) {
967  // If we're not in sequential mode, we split at the mid point
968  // of the node.
969  m = mid_point(p);
970  } else {
971  // During sequential addition, split at the insert point
972  AssertRel(c,>=,DIR_START);
973  m = c;
974  }
975 
976  uint4 split_n = C[j].get_n();
977  C[j].set_n(free_list.get_block(this, block_size));
978 
979  memcpy(split_p, p, block_size); // replicate the whole block in split_p
980  SET_DIR_END(split_p, m);
981  compact(split_p); /* to reset TOTAL_FREE, MAX_FREE */
982 
983  {
984  int residue = DIR_END(p) - m;
985  int new_dir_end = DIR_START + residue;
986  memmove(p + DIR_START, p + m, residue);
987  SET_DIR_END(p, new_dir_end);
988  }
989 
990  compact(p); /* to reset TOTAL_FREE, MAX_FREE */
991 
992  bool add_to_upper_half;
993  if (seq_count < 0) {
994  add_to_upper_half = (c >= m);
995  } else {
996  // And add item to lower half if split_p has room, otherwise upper
997  // half
998  add_to_upper_half = (TOTAL_FREE(split_p) < needed);
999  }
1000 
1001  if (add_to_upper_half) {
1002  c -= (m - DIR_START);
1003  Assert(seq_count < 0 || c <= DIR_START + D2);
1004  Assert(c >= DIR_START);
1005  Assert(c <= DIR_END(p));
1006  add_item_to_branch(p, kt_, c);
1007  } else {
1008  Assert(c >= DIR_START);
1009  Assert(c <= DIR_END(split_p));
1010  add_item_to_branch(split_p, kt_, c);
1011  }
1012  write_block(split_n, split_p);
1013 
1014  // Check if we're splitting the root block.
1015  if (j == level) split_root(split_n);
1016 
1017  /* Enter a separating key at level j + 1 between */
1018  /* the last key of block split_p, and the first key of block p */
1019  enter_key_above_branch(j + 1, BItem(p, DIR_START));
1020 
1021  // In branch levels, we can make the first key of block p null and
1022  // save a bit of disk space. Other redundant keys will still creep
1023  // in though.
1024  BItem_wr item(p, DIR_START);
1025  int new_total_free = TOTAL_FREE(p) + item.key().length();
1026  item.form_null_key(item.block_given_by());
1027  SET_TOTAL_FREE(p, new_total_free);
1028  } else {
1029  AssertRel(TOTAL_FREE(p),>=,needed);
1030 
1031  if (MAX_FREE(p) < needed) {
1032  compact(p);
1033  AssertRel(MAX_FREE(p),>=,needed);
1034  }
1035 
1036  add_item_to_branch(p, kt_, c);
1037  }
1038 }
1039 
1047 void
1049 {
1050  LOGCALL_VOID(DB, "GlassTable::delete_leaf_item", repeatedly);
1051  Assert(writable);
1052  uint8_t * p = C[0].get_modifiable_p(block_size);
1053  int c = C[0].c;
1054  AssertRel(DIR_START,<=,c);
1055  AssertRel(c,<,DIR_END(p));
1056  int kt_len = LeafItem(p, c).size(); /* size of the item to be deleted */
1057  int dir_end = DIR_END(p) - D2; /* directory length will go down by 2 bytes */
1058 
1059  memmove(p + c, p + c + D2, dir_end - c);
1060  SET_DIR_END(p, dir_end);
1061  SET_MAX_FREE(p, MAX_FREE(p) + D2);
1062  SET_TOTAL_FREE(p, TOTAL_FREE(p) + kt_len + D2);
1063 
1064  if (!repeatedly) return;
1065  if (0 < level) {
1066  if (dir_end == DIR_START) {
1067  free_list.mark_block_unused(this, block_size, C[0].get_n());
1068  C[0].rewrite = false;
1069  C[0].set_n(BLK_UNUSED);
1070  C[1].rewrite = true; /* *is* necessary */
1071  delete_branch_item(1);
1072  }
1073  }
1074 }
1075 
1082 void
1084 {
1085  LOGCALL_VOID(DB, "GlassTable::delete_branch_item", j);
1086  Assert(writable);
1087  uint8_t * p = C[j].get_modifiable_p(block_size);
1088  int c = C[j].c;
1089  AssertRel(DIR_START,<=,c);
1090  AssertRel(c,<,DIR_END(p));
1091  int kt_len = BItem(p, c).size(); /* size of the item to be deleted */
1092  int dir_end = DIR_END(p) - D2; /* directory length will go down by 2 bytes */
1093 
1094  memmove(p + c, p + c + D2, dir_end - c);
1095  SET_DIR_END(p, dir_end);
1096  SET_MAX_FREE(p, MAX_FREE(p) + D2);
1097  SET_TOTAL_FREE(p, TOTAL_FREE(p) + kt_len + D2);
1098 
1099  if (j < level) {
1100  if (dir_end == DIR_START) {
1101  free_list.mark_block_unused(this, block_size, C[j].get_n());
1102  C[j].rewrite = false;
1103  C[j].set_n(BLK_UNUSED);
1104  C[j + 1].rewrite = true; /* *is* necessary */
1105  delete_branch_item(j + 1);
1106  }
1107  } else {
1108  Assert(j == level);
1109  while (dir_end == DIR_START + D2 && level > 0) {
1110  /* single item in the root block, so lose a level */
1111  uint4 new_root = BItem(C[level].get_p(), DIR_START).block_given_by();
1112  free_list.mark_block_unused(this, block_size, C[level].get_n());
1113  C[level].destroy();
1114  level--;
1115 
1116  block_to_cursor(C, level, new_root);
1117 
1118  dir_end = DIR_END(C[level].get_p()); /* prepare for the loop */
1119  }
1120  }
1121 }
1122 
1123 /* debugging aid:
1124 static addcount = 0;
1125 */
1126 
1157 int
1159 {
1160  LOGCALL(DB, int, "GlassTable::add_kt", found);
1161  Assert(writable);
1162 
1163  /*
1164  {
1165  printf("%d) %s ", addcount++, (found ? "replacing" : "adding"));
1166  print_bytes(kt[I2], kt + I2 + K1); putchar('\n');
1167  }
1168  */
1169  alter();
1170 
1171  int result = 0;
1172  if (found) { /* replacement */
1173  seq_count = SEQ_START_POINT;
1174  sequential = false;
1175 
1176  uint8_t * p = C[0].get_modifiable_p(block_size);
1177  int c = C[0].c;
1178  AssertRel(DIR_START,<=,c);
1179  AssertRel(c,<,DIR_END(p));
1180  LeafItem item(p, c);
1181  int kt_size = kt.size();
1182  int needed = kt_size - item.size();
1183 
1184  result = item.last_component() ? 2 : 1;
1185 
1186  if (needed <= 0) {
1187  /* simple replacement */
1188  memmove(const_cast<uint8_t *>(item.get_address()),
1189  kt.get_address(), kt_size);
1190  SET_TOTAL_FREE(p, TOTAL_FREE(p) - needed);
1191  } else {
1192  /* new item into the block's freespace */
1193  int new_max = MAX_FREE(p) - kt_size;
1194  if (new_max >= 0) {
1195  int o = DIR_END(p) + new_max;
1196  memmove(p + o, kt.get_address(), kt_size);
1197  LeafItem_wr::setD(p, c, o);
1198  SET_MAX_FREE(p, new_max);
1199  SET_TOTAL_FREE(p, TOTAL_FREE(p) - needed);
1200  } else {
1201  /* do it the long way */
1202  delete_leaf_item(false);
1203  add_leaf_item(kt);
1204  }
1205  }
1206  } else {
1207  /* addition */
1208  if (changed_n == C[0].get_n() && changed_c == C[0].c) {
1209  if (seq_count < 0) seq_count++;
1210  } else {
1211  seq_count = SEQ_START_POINT;
1212  sequential = false;
1213  }
1214  C[0].c += D2;
1215  add_leaf_item(kt);
1216  }
1217  RETURN(result);
1218 }
1219 
1220 /* delete_kt() corresponds to add_kt(found), but there are only
1221  two cases: if the key is not found nothing is done, and if it is
1222  found the corresponding item is deleted with delete_leaf_item.
1223 
1224  Returns:
1225  0 : nothing to delete
1226  1 : deleted kt
1227  2 : deleted kt and it was the final one
1228 */
1229 
1230 int
1232 {
1233  LOGCALL(DB, int, "GlassTable::delete_kt", NO_ARGS);
1234  Assert(writable);
1235 
1236  seq_count = SEQ_START_POINT;
1237  sequential = false;
1238 
1239  if (!find(C))
1240  return 0;
1241 
1242  int result = LeafItem(C[0].get_p(), C[0].c).last_component() ? 2 : 1;
1243  alter();
1244  delete_leaf_item(true);
1245 
1246  RETURN(result);
1247 }
1248 
1249 /* GlassTable::form_key(key) treats address kt as an item holder and fills in
1250 the key part:
1251 
1252  (I) K key c (C tag)
1253 
1254 The bracketed parts are left blank. The key is filled in with key_len bytes and
1255 K set accordingly. c is set to 1.
1256 */
1257 
1258 void GlassTable::form_key(string_view key) const
1259 {
1260  LOGCALL_VOID(DB, "GlassTable::form_key", key);
1261  kt.form_key(key);
1262 }
1263 
1264 /* GlassTable::add(key, tag) adds the key/tag item to the
1265  B-tree, replacing any existing item with the same key.
1266 
1267  For a long tag, we end up having to add m components, of the form
1268 
1269  key 1 m tag1
1270  key 2 m tag2
1271  ...
1272  key m m tagm
1273 
1274  and tag1+tag2+...+tagm are equal to tag. These in their turn may be replacing
1275  n components of the form
1276 
1277  key 1 n TAG1
1278  key 2 n TAG2
1279  ...
1280  key n n TAGn
1281 
1282  and n may be greater than, equal to, or less than m. These cases are dealt
1283  with in the code below. If m < n for example, we end up with a series of
1284  deletions.
1285 */
1286 
1287 void
1288 GlassTable::add(string_view key, string_view tag, bool already_compressed)
1289 {
1290  LOGCALL_VOID(DB, "GlassTable::add", key | tag | already_compressed);
1291  Assert(writable);
1292 
1293  if (handle < 0) {
1294  if (handle == -2) {
1296  }
1297  RootInfo root_info;
1298  root_info.init(block_size, compress_min);
1299  do_open_to_write(&root_info);
1300  }
1301 
1302  form_key(key);
1303 
1304  const char* tag_data = tag.data();
1305  size_t tag_size = tag.size();
1306 
1307  bool compressed = false;
1308  if (already_compressed) {
1309  compressed = true;
1310  } else if (compress_min > 0 && tag_size > compress_min) {
1311  const char * res = comp_stream.compress(tag_data, &tag_size);
1312  if (res) {
1313  compressed = true;
1314  tag_data = res;
1315  }
1316  }
1317 
1318  // sort of matching kt.append_chunk(), but setting the chunk
1319  const size_t cd = kt.key().length() + K1 + I2 + X2; // offset to the tag data
1320  const size_t L = max_item_size - cd; // largest amount of tag data for any chunk
1321  size_t first_L = L + X2; // - amount for tag1 (we don't store X there)
1322  bool found = find(C);
1323  if (tag_size <= first_L) {
1324  // The whole tag clearly fits in one item, so no need to make this
1325  // complicated.
1326  first_L = tag_size;
1327  } else if (!found) {
1328  const uint8_t * p = C[0].get_p();
1329  size_t n = TOTAL_FREE(p) % (max_item_size + D2);
1330  if (n > D2 + cd) {
1331  n -= (D2 + cd);
1332  // if n >= last then fully filling this block won't produce
1333  // an extra item, so we might as well do this even if
1334  // full_compaction isn't active.
1335  //
1336  // In the full_compaction case, it turns out we shouldn't always
1337  // try to fill every last byte. Doing so can actually increase the
1338  // total space required (I believe this effect is due to longer
1339  // dividing keys being required in the index blocks). Empirically,
1340  // n >= key.size() + K appears a good criterion for K ~= 34. This
1341  // seems to save about 0.2% in total database size over always
1342  // splitting the tag. It'll also give be slightly faster retrieval
1343  // as we can avoid reading an extra block occasionally.
1344  size_t last = (tag_size - X2) % L;
1345  if (n >= last || (full_compaction && n >= key.size() + 34)) {
1346  // first_L < max_item_size + D2 - D2 - cd
1347  // Total size of first item = cd + first_L < max_item_size
1348  first_L = n + X2;
1349  }
1350  }
1351  }
1352 
1353  // There are m items to add.
1354  int m = (tag_size - first_L + L - 1) / L + 1;
1355  /* FIXME: sort out this error higher up and turn this into
1356  * an assert.
1357  */
1358  if (m >= BYTE_PAIR_RANGE) {
1359  string message = "Btree tag entry of size ";
1360  message += str(tag_size);
1361  message += " is too large to store - "
1362  "increase the block size to raise this limit";
1363  throw Xapian::UnimplementedError(message);
1364  }
1365 
1366  size_t o = 0; // Offset into the tag
1367  size_t residue = tag_size; // Bytes of the tag remaining to add in
1368  bool replacement = false; // Has there been a replacement?
1369  bool components_to_del = false; // Are there components to delete?
1370  int i;
1371  for (i = 1; i <= m; ++i) {
1372  size_t l = (i == m ? residue : (i == 1 ? first_L : L));
1373  size_t this_cd = (i == 1 ? cd - X2 : cd);
1374  Assert(this_cd + l <= block_size);
1375  Assert(o + l <= tag_size);
1376  kt.set_tag(this_cd, tag_data + o, l, compressed, i, m);
1377 
1378  o += l;
1379  residue -= l;
1380 
1381  if (i > 1) found = find(C);
1382  int result = add_kt(found);
1383  if (result) replacement = true;
1384  components_to_del = (result == 1);
1385  }
1386  AssertEq(o, tag_size);
1387  if (components_to_del) {
1388  i = m;
1389  do {
1390  kt.set_component_of(++i);
1391  } while (delete_kt() == 1);
1392  }
1393  if (!replacement) ++item_count;
1394  Btree_modified = true;
1395  if (cursor_created_since_last_modification) {
1396  cursor_created_since_last_modification = false;
1397  ++cursor_version;
1398  }
1399 }
1400 
1401 /* GlassTable::del(key) returns false if the key is not in the B-tree,
1402  otherwise deletes it and returns true.
1403 
1404  Again, this is parallel to GlassTable::add, but simpler in form.
1405 */
1406 
1407 bool
1408 GlassTable::del(string_view key)
1409 {
1410  LOGCALL(DB, bool, "GlassTable::del", key);
1411  Assert(writable);
1412 
1413  if (handle < 0) {
1414  if (handle == -2) {
1416  }
1417  RETURN(false);
1418  }
1419 
1420  // We can't delete a key which we is too long for us to store.
1421  if (key.size() > GLASS_BTREE_MAX_KEY_LEN) RETURN(false);
1422 
1423  if (key.empty()) RETURN(false);
1424  form_key(key);
1425 
1426  int r = delete_kt();
1427  if (r == 0) RETURN(false);
1428  int i = 1;
1429  while (r == 1) {
1430  kt.set_component_of(++i);
1431  r = delete_kt();
1432  }
1433 
1434  item_count--;
1435  Btree_modified = true;
1436  if (cursor_created_since_last_modification) {
1437  cursor_created_since_last_modification = false;
1438  ++cursor_version;
1439  }
1440  RETURN(true);
1441 }
1442 
1443 bool
1444 GlassTable::readahead_key(string_view key) const
1445 {
1446  LOGCALL(DB, bool, "GlassTable::readahead_key", key);
1447  Assert(!key.empty());
1448 
1449  // Three cases:
1450  //
1451  // handle == -1: Lazy table in a multi-file database which isn't yet open.
1452  //
1453  // handle == -2: Table has been closed. Since the readahead is just a
1454  // hint, we can safely ignore it for a closed table.
1455  //
1456  // handle <= -3: Lazy table in a single-file database which isn't yet
1457  // open.
1458  if (handle < 0)
1459  RETURN(false);
1460 
1461  // If the table only has one level, there are no branch blocks to preread.
1462  if (level == 0)
1463  RETURN(false);
1464 
1465  // An overlong key cannot be found.
1466  if (key.size() > GLASS_BTREE_MAX_KEY_LEN)
1467  RETURN(true);
1468 
1469  form_key(key);
1470 
1471  // We'll only readahead the first level, since descending the B-tree would
1472  // require actual reads that would likely hurt performance more than help.
1473  const uint8_t * p = C[level].get_p();
1474  int c = find_in_branch(p, kt, C[level].c);
1475  uint4 n = BItem(p, c).block_given_by();
1476  // Don't preread if it's the block we last preread or already in the
1477  // cursor.
1478  if (n != last_readahead && n != C[level - 1].get_n()) {
1479  last_readahead = n;
1480  if (!io_readahead_block(handle, block_size, n, offset))
1481  RETURN(false);
1482  }
1483  RETURN(true);
1484 }
1485 
1486 bool
1487 GlassTable::get_exact_entry(string_view key, string& tag) const
1488 {
1489  LOGCALL(DB, bool, "GlassTable::get_exact_entry", key | tag);
1490  Assert(!key.empty());
1491 
1492  if (handle < 0) {
1493  if (handle == -2) {
1495  }
1496  RETURN(false);
1497  }
1498 
1499  // An oversized key can't exist, so attempting to search for it should fail.
1500  if (key.size() > GLASS_BTREE_MAX_KEY_LEN) RETURN(false);
1501 
1502  form_key(key);
1503  if (!find(C)) RETURN(false);
1504 
1505  (void)read_tag(C, &tag, false);
1506  RETURN(true);
1507 }
1508 
1509 bool
1510 GlassTable::key_exists(string_view key) const
1511 {
1512  LOGCALL(DB, bool, "GlassTable::key_exists", key);
1513  Assert(!key.empty());
1514 
1515  // An oversized key can't exist, so attempting to search for it should fail.
1516  if (key.size() > GLASS_BTREE_MAX_KEY_LEN) RETURN(false);
1517 
1518  form_key(key);
1519  RETURN(find(C));
1520 }
1521 
1522 bool
1523 GlassTable::read_tag(Glass::Cursor * C_, string *tag, bool keep_compressed) const
1524 {
1525  LOGCALL(DB, bool, "GlassTable::read_tag", Literal("C_") | tag | keep_compressed);
1526 
1527  tag->resize(0);
1528 
1529  LeafItem item(C_[0].get_p(), C_[0].c);
1530  bool compressed = item.get_compressed();
1531  bool decompress = false;
1532  if (compressed && !keep_compressed) {
1533  comp_stream.decompress_start();
1534  decompress = true;
1535  }
1536 
1537  while (true) {
1538  bool last = item.last_component();
1539  if (decompress) {
1540  // Decompress each chunk as we read it so we don't need both the
1541  // full compressed and uncompressed tags in memory at once.
1542  bool done = item.decompress_chunk(comp_stream, *tag);
1543  if (done != last) {
1544  throw Xapian::DatabaseCorruptError(done ?
1545  "Too many chunks of compressed data" :
1546  "Too few chunks of compressed data");
1547  }
1548  } else {
1549  item.append_chunk(tag);
1550  }
1551  if (last) break;
1552  if (!next(C_, 0)) {
1553  throw Xapian::DatabaseCorruptError("Unexpected end of table when reading continuation of tag");
1554  }
1555  item.init(C_[0].get_p(), C_[0].c);
1556  }
1557  // At this point the cursor is on the last item - calling next will move
1558  // it to the next key (GlassCursor::read_tag() relies on this).
1559 
1560  RETURN(compressed && keep_compressed);
1561 }
1562 
1563 void
1565 {
1566  LOGCALL_VOID(DB, "GlassTable::set_full_compaction", parity);
1567  Assert(writable);
1568 
1569  if (parity) seq_count = 0;
1570  full_compaction = parity;
1571 }
1572 
1574  LOGCALL(DB, GlassCursor *, "GlassTable::cursor_get", NO_ARGS);
1575  if (handle < 0) {
1576  if (handle == -2) {
1578  }
1579  RETURN(NULL);
1580  }
1581  // FIXME Ick - casting away const is nasty
1582  RETURN(new GlassCursor(const_cast<GlassTable *>(this)));
1583 }
1584 
1585 /************ B-tree opening and closing ************/
1586 
1587 void
1589 {
1590  LOGCALL_VOID(DB, "GlassTable::basic_open", root_info|rev);
1591  revision_number = rev;
1592  root = root_info->get_root();
1593  level = root_info->get_level();
1594  item_count = root_info->get_num_entries();
1595  faked_root_block = root_info->get_root_is_fake();
1596  sequential = root_info->get_sequential();
1597  const string & fl_serialised = root_info->get_free_list();
1598  if (!fl_serialised.empty()) {
1599  if (!free_list.unpack(fl_serialised))
1600  throw Xapian::DatabaseCorruptError("Bad freelist metadata");
1601  } else {
1602  free_list.reset();
1603  }
1604 
1605  compress_min = root_info->get_compress_min();
1606 
1607  /* kt holds constructed items as well as keys */
1608  kt = LeafItem_wr(zeroed_new(block_size));
1609 
1610  set_max_item_size(BLOCK_CAPACITY);
1611 
1612  for (int j = 0; j <= level; ++j) {
1613  C[j].init(block_size);
1614  }
1615 
1616  read_root();
1617 
1618  if (cursor_created_since_last_modification) {
1619  cursor_created_since_last_modification = false;
1620  ++cursor_version;
1621  }
1622 }
1623 
1624 void
1626 {
1627  LOGCALL_VOID(DB, "GlassTable::read_root", NO_ARGS);
1628  if (faked_root_block) {
1629  /* root block for an unmodified database. */
1630  uint8_t * p = C[0].init(block_size);
1631  Assert(p);
1632 
1633  /* clear block - shouldn't be necessary, but is a bit nicer,
1634  * and means that the same operations should always produce
1635  * the same database. */
1636  memset(p, 0, block_size);
1637 
1638  int o = block_size - I2 - K1;
1639  LeafItem_wr(p + o).fake_root_item();
1640 
1641  LeafItem_wr::setD(p, DIR_START, o); // its directory entry
1642  SET_DIR_END(p, DIR_START + D2);// the directory size
1643 
1644  o -= (DIR_START + D2);
1645  SET_MAX_FREE(p, o);
1646  SET_TOTAL_FREE(p, o);
1647  SET_LEVEL(p, 0);
1648 
1649  if (!writable) {
1650  /* reading - revision number doesn't matter as long as
1651  * it's not greater than the current one. */
1652  SET_REVISION(p, 0);
1653  C[0].set_n(0);
1654  } else {
1655  /* writing - */
1656  SET_REVISION(p, revision_number + 1);
1657  C[0].set_n(free_list.get_block(this, block_size));
1658  C[0].rewrite = true;
1659  }
1660  } else {
1661  /* using a root block stored on disk */
1662  block_to_cursor(C, level, root);
1663 
1664  if (REVISION(C[level].get_p()) > revision_number) throw_overwritten();
1665  /* although this is unlikely */
1666  }
1667 }
1668 
1669 void
1672 {
1673  LOGCALL_VOID(DB, "GlassTable::do_open_to_write", root_info|rev);
1674  if (handle == -2) {
1676  }
1677  if (handle <= -2) {
1678  // Single file database.
1679  handle = -3 - handle;
1680  } else {
1681  handle = io_open_block_wr(name + GLASS_TABLE_EXTENSION, (rev == 0));
1682  if (handle < 0) {
1683  // lazy doesn't make a lot of sense when we're creating a DB (which
1684  // is the case when rev==0), but ENOENT with O_CREAT means a parent
1685  // directory doesn't exist.
1686  if (lazy && rev && errno == ENOENT) {
1687  revision_number = rev;
1688  return;
1689  }
1690  string message((rev == 0) ? "Couldn't create " : "Couldn't open ");
1691  message += name;
1692  message += GLASS_TABLE_EXTENSION" read/write";
1693  throw Xapian::DatabaseOpeningError(message, errno);
1694  }
1695  }
1696 
1697  writable = true;
1698  basic_open(root_info, rev);
1699 
1700  split_p = new uint8_t[block_size];
1701 
1702  buffer = zeroed_new(block_size);
1703 
1704  changed_n = 0;
1705  changed_c = DIR_START;
1706  seq_count = SEQ_START_POINT;
1707 }
1708 
1709 GlassTable::GlassTable(const char* tablename_, string_view path_,
1710  bool readonly_, bool lazy_)
1711  : tablename(tablename_),
1712  revision_number(0),
1713  item_count(0),
1714  block_size(0),
1715  faked_root_block(true),
1716  sequential(true),
1717  handle(-1),
1718  level(0),
1719  root(0),
1720  kt(0),
1721  buffer(0),
1722  free_list(),
1723  name(path_),
1724  seq_count(0),
1725  changed_n(0),
1726  changed_c(0),
1727  max_item_size(0),
1728  Btree_modified(false),
1729  full_compaction(false),
1730  writable(!readonly_),
1731  cursor_created_since_last_modification(false),
1732  cursor_version(0),
1733  changes_obj(NULL),
1734  split_p(0),
1735  compress_min(0),
1736  comp_stream(Z_DEFAULT_STRATEGY),
1737  lazy(lazy_),
1738  last_readahead(BLK_UNUSED),
1739  offset(0)
1740 {
1741  LOGCALL_CTOR(DB, "GlassTable", tablename_ | path_ | readonly_ | lazy_);
1742 }
1743 
1744 GlassTable::GlassTable(const char * tablename_, int fd, off_t offset_,
1745  bool readonly_, bool lazy_)
1746  : tablename(tablename_),
1747  revision_number(0),
1748  item_count(0),
1749  block_size(0),
1750  faked_root_block(true),
1751  sequential(true),
1752  handle(-3 - fd),
1753  level(0),
1754  root(0),
1755  kt(0),
1756  buffer(0),
1757  free_list(),
1758  name(),
1759  seq_count(0),
1760  changed_n(0),
1761  changed_c(0),
1762  max_item_size(0),
1763  Btree_modified(false),
1764  full_compaction(false),
1765  writable(!readonly_),
1766  cursor_created_since_last_modification(false),
1767  cursor_version(0),
1768  changes_obj(NULL),
1769  split_p(0),
1770  compress_min(0),
1771  comp_stream(Z_DEFAULT_STRATEGY),
1772  lazy(lazy_),
1773  last_readahead(BLK_UNUSED),
1774  offset(offset_)
1775 {
1776  LOGCALL_CTOR(DB, "GlassTable", tablename_ | fd | offset_ | readonly_ | lazy_);
1777 }
1778 
1779 bool
1781  LOGCALL(DB, bool, "GlassTable::exists", NO_ARGS);
1782  // We know a single-file database exists, since we have an fd open on it!
1784 }
1785 
1786 void
1787 GlassTable::create_and_open(int flags_, const RootInfo & root_info)
1788 {
1789  LOGCALL_VOID(DB, "GlassTable::create_and_open", flags_|root_info);
1790  if (handle == -2) {
1792  }
1793  Assert(writable);
1794  close();
1795 
1796  unsigned int block_size_ = root_info.get_blocksize();
1797  Assert(block_size_ >= GLASS_MIN_BLOCKSIZE);
1798  Assert(block_size_ <= BYTE_PAIR_RANGE);
1799  // Must be a power of two.
1800  Assert((block_size_ & (block_size_ - 1)) == 0);
1801 
1802  flags = flags_;
1803  block_size = block_size_;
1804 
1805  if (lazy) {
1806  close();
1808  compress_min = root_info.get_compress_min();
1809  } else {
1810  // FIXME: it would be good to arrange that this works such that there's
1811  // always a valid table in place if you run create_and_open() on an
1812  // existing table.
1813 
1814  do_open_to_write(&root_info);
1815  }
1816 }
1817 
1819  LOGCALL_DTOR(DB, "GlassTable");
1821 }
1822 
1823 void GlassTable::close(bool permanent) {
1824  LOGCALL_VOID(DB, "GlassTable::close", permanent);
1825 
1826  if (handle >= 0) {
1827  if (single_file()) {
1828  handle = -3 - handle;
1829  } else {
1830  // If an error occurs here, we just ignore it, since we're just
1831  // trying to free everything.
1832  (void)::close(handle);
1833  handle = -1;
1834  }
1835  }
1836 
1837  if (permanent) {
1838  handle = -2;
1839  // Don't delete the resources in the table, since they may
1840  // still be used to look up cached content.
1841  return;
1842  }
1843  for (int j = level; j >= 0; --j) {
1844  C[j].destroy();
1845  }
1846  delete [] split_p;
1847  split_p = 0;
1848 
1849  delete [] kt.get_address();
1850  kt = LeafItem_wr(0);
1851  delete [] buffer;
1852  buffer = 0;
1853 }
1854 
1855 void
1857 {
1858  LOGCALL_VOID(DB, "GlassTable::flush_db", NO_ARGS);
1859  Assert(writable);
1860  if (handle < 0) {
1861  if (handle == -2) {
1863  }
1864  return;
1865  }
1866 
1867  for (int j = level; j >= 0; --j) {
1868  if (C[j].rewrite) {
1869  write_block(C[j].get_n(), C[j].get_p());
1870  }
1871  }
1872 
1873  faked_root_block = false;
1874 }
1875 
1876 void
1878 {
1879  LOGCALL_VOID(DB, "GlassTable::commit", revision|root_info);
1880  Assert(writable);
1881 
1882  if (revision <= revision_number) {
1883  throw Xapian::DatabaseError("New revision too low");
1884  }
1885 
1886  if (handle < 0) {
1887  if (handle == -2) {
1889  }
1891  root_info->set_blocksize(block_size);
1892  root_info->set_level(0);
1893  root_info->set_num_entries(0);
1894  root_info->set_root_is_fake(true);
1895  root_info->set_sequential(true);
1896  root_info->set_root(0);
1897  return;
1898  }
1899 
1900  try {
1901  root = C[level].get_n();
1902 
1903  root_info->set_blocksize(block_size);
1904  root_info->set_level(level);
1905  root_info->set_num_entries(item_count);
1906  root_info->set_root_is_fake(faked_root_block);
1907  root_info->set_sequential(sequential);
1908  root_info->set_root(root);
1909 
1910  Btree_modified = false;
1911 
1912  for (int i = 0; i <= level; ++i) {
1913  C[i].init(block_size);
1914  }
1915 
1917  free_list.commit(this, block_size);
1918 
1919  // Save the freelist details into the root_info.
1920  string serialised;
1921  free_list.pack(serialised);
1922  root_info->set_free_list(serialised);
1923 
1925 
1926  read_root();
1927 
1928  changed_n = 0;
1929  changed_c = DIR_START;
1931  } catch (...) {
1933  throw;
1934  }
1935 }
1936 
1937 void
1939 {
1940  LOGCALL_VOID(DB, "GlassTable::cancel", root_info|rev);
1941  Assert(writable);
1942 
1943  if (handle < 0) {
1944  if (handle == -2) {
1946  }
1947  return;
1948  }
1949 
1950  // This causes problems: if (!Btree_modified) return;
1951 
1953  throw Xapian::InvalidOperationError("cancel() not supported under Xapian::DB_DANGEROUS");
1954 
1955  revision_number = rev;
1956  block_size = root_info.get_blocksize();
1957  root = root_info.get_root();
1958  level = root_info.get_level();
1959  item_count = root_info.get_num_entries();
1960  faked_root_block = root_info.get_root_is_fake();
1961  sequential = root_info.get_sequential();
1962  const string & fl_serialised = root_info.get_free_list();
1963  if (!fl_serialised.empty()) {
1964  if (!free_list.unpack(fl_serialised))
1965  throw Xapian::DatabaseCorruptError("Bad freelist metadata");
1966  } else {
1967  free_list.reset();
1968  }
1969 
1970  Btree_modified = false;
1971 
1972  for (int j = 0; j <= level; ++j) {
1973  C[j].init(block_size);
1974  C[j].rewrite = false;
1975  }
1976  read_root();
1977 
1978  changed_n = 0;
1979  changed_c = DIR_START;
1981 
1984  ++cursor_version;
1985  }
1986 }
1987 
1988 /************ B-tree reading ************/
1989 
1990 void
1993 {
1994  LOGCALL(DB, bool, "GlassTable::do_open_to_read", root_info|rev);
1995  if (handle == -2) {
1997  }
1998  if (single_file()) {
1999  handle = -3 - handle;
2000  } else {
2002  if (handle < 0) {
2003  if (lazy) {
2004  // This table is optional when reading!
2005  revision_number = rev;
2006  return;
2007  }
2008  string message("Couldn't open ");
2009  message += name;
2010  message += GLASS_TABLE_EXTENSION" to read";
2011  throw Xapian::DatabaseOpeningError(message, errno);
2012  }
2013  }
2014 
2015  basic_open(root_info, rev);
2016 
2017  read_root();
2018 }
2019 
2020 void
2021 GlassTable::open(int flags_, const RootInfo & root_info,
2023 {
2024  LOGCALL_VOID(DB, "GlassTable::open", flags_|root_info|rev);
2025  close();
2026 
2027  flags = flags_;
2028  block_size = root_info.get_blocksize();
2029  root = root_info.get_root();
2030 
2031  if (!writable) {
2032  do_open_to_read(&root_info, rev);
2033  return;
2034  }
2035 
2036  do_open_to_write(&root_info, rev);
2037 }
2038 
2039 bool
2041 {
2042  LOGCALL(DB, bool, "GlassTable::prev_for_sequential", Literal("C_") | Literal("/*dummy*/"));
2043  int c = C_[0].c;
2044  AssertRel(DIR_START,<=,c);
2045  AssertRel(c,<,DIR_END(C_[0].get_p()));
2046  if (c == DIR_START) {
2047  uint4 n = C_[0].get_n();
2048  const uint8_t * p;
2049  while (true) {
2050  if (n == 0) RETURN(false);
2051  n--;
2052  if (n == C[0].get_n()) {
2053  // Block is a leaf block in the built-in cursor (potentially in
2054  // modified form if the table is writable).
2055  p = C_[0].clone(C[0]);
2056  } else {
2057  if (writable) {
2058  // Blocks in the built-in cursor may not have been written
2059  // to disk yet, so we have to check that the block number
2060  // isn't in the built-in cursor or we'll read an
2061  // uninitialised block (for which GET_LEVEL(p) will
2062  // probably return 0).
2063  int j;
2064  for (j = 1; j <= level; ++j) {
2065  if (n == C[j].get_n()) break;
2066  }
2067  if (j <= level) continue;
2068  }
2069 
2070  // Block isn't in the built-in cursor, so the form on disk
2071  // is valid, so read it to check if it's the next level 0
2072  // block.
2073  uint8_t * q = C_[0].init(block_size);
2074  read_block(n, q);
2075  p = q;
2076  C_[0].set_n(n);
2077  }
2078  if (REVISION(p) > revision_number + writable) {
2080  }
2081  if (GET_LEVEL(p) == 0) break;
2082  }
2083  c = DIR_END(p);
2084  AssertRel(DIR_START,<,c);
2085  }
2086  c -= D2;
2087  C_[0].c = c;
2088  RETURN(true);
2089 }
2090 
2091 bool
2093 {
2094  LOGCALL(DB, bool, "GlassTable::next_for_sequential", Literal("C_") | Literal("/*dummy*/"));
2095  const uint8_t * p = C_[0].get_p();
2096  Assert(p);
2097  int c = C_[0].c;
2098  AssertRel(c,<,DIR_END(p));
2099  c += D2;
2100  Assert(unsigned(c) < block_size);
2101  if (c == DIR_END(p)) {
2102  uint4 n = C_[0].get_n();
2103  while (true) {
2104  n++;
2105  if (n >= free_list.get_first_unused_block()) RETURN(false);
2106  if (writable) {
2107  if (n == C[0].get_n()) {
2108  // Block is a leaf block in the built-in cursor
2109  // (potentially in modified form).
2110  p = C_[0].clone(C[0]);
2111  } else {
2112  // Blocks in the built-in cursor may not have been written
2113  // to disk yet, so we have to check that the block number
2114  // isn't in the built-in cursor or we'll read an
2115  // uninitialised block (for which GET_LEVEL(p) will
2116  // probably return 0).
2117  int j;
2118  for (j = 1; j <= level; ++j) {
2119  if (n == C[j].get_n()) break;
2120  }
2121  if (j <= level) continue;
2122 
2123  // Block isn't in the built-in cursor, so the form on disk
2124  // is valid, so read it to check if it's the next level 0
2125  // block.
2126  uint8_t * q = C_[0].init(block_size);
2127  read_block(n, q);
2128  p = q;
2129  }
2130  } else {
2131  uint8_t * q = C_[0].init(block_size);
2132  read_block(n, q);
2133  p = q;
2134  }
2135  if (REVISION(p) > revision_number + writable) {
2137  }
2138  if (GET_LEVEL(p) == 0) break;
2139  }
2140  c = DIR_START;
2141  C_[0].set_n(n);
2142  }
2143  C_[0].c = c;
2144  RETURN(true);
2145 }
2146 
2147 bool
2149 {
2150  LOGCALL(DB, bool, "GlassTable::prev_default", Literal("C_") | j);
2151  const uint8_t * p = C_[j].get_p();
2152  int c = C_[j].c;
2153  AssertRel(DIR_START,<=,c);
2154  AssertRel(c,<,DIR_END(p));
2155  AssertRel(unsigned(DIR_END(p)),<=,block_size);
2156  if (c == DIR_START) {
2157  if (j == level) RETURN(false);
2158  if (!prev_default(C_, j + 1)) RETURN(false);
2159  p = C_[j].get_p();
2160  c = DIR_END(p);
2161  AssertRel(DIR_START,<,c);
2162  }
2163  c -= D2;
2164  C_[j].c = c;
2165  if (j > 0) {
2166  block_to_cursor(C_, j - 1, BItem(p, c).block_given_by());
2167  }
2168  RETURN(true);
2169 }
2170 
2171 bool
2173 {
2174  LOGCALL(DB, bool, "GlassTable::next_default", Literal("C_") | j);
2175  const uint8_t * p = C_[j].get_p();
2176  int c = C_[j].c;
2177  AssertRel(c,<,DIR_END(p));
2178  AssertRel(unsigned(DIR_END(p)),<=,block_size);
2179  c += D2;
2180  if (j > 0) {
2181  AssertRel(DIR_START,<,c);
2182  } else {
2183  AssertRel(DIR_START,<=,c);
2184  }
2185  // Sometimes c can be DIR_END(p) + 2 here it appears...
2186  if (c >= DIR_END(p)) {
2187  if (j == level) RETURN(false);
2188  if (!next_default(C_, j + 1)) RETURN(false);
2189  p = C_[j].get_p();
2190  c = DIR_START;
2191  }
2192  C_[j].c = c;
2193  if (j > 0) {
2194  block_to_cursor(C_, j - 1, BItem(p, c).block_given_by());
2195 #ifdef BTREE_DEBUG_FULL
2196  printf("Block in GlassTable:next_default");
2197  report_block_full(j - 1, C_[j - 1].get_n(), C_[j - 1].get_p());
2198 #endif /* BTREE_DEBUG_FULL */
2199  }
2200  RETURN(true);
2201 }
2202 
2203 void
2205 {
2206  throw Xapian::DatabaseClosedError("Database has been closed");
2207 }
2208 
2209 #ifdef DISABLE_GPL_LIBXAPIAN
2210 # error GPL source we cannot relicense included in libxapian
2211 #endif
char name[9]
Definition: dbcheck.cc:57
A cursor pointing to a position in a Btree table, for reading several entries in order,...
Definition: glass_cursor.h:148
void pack(std::string &buf)
uint4 get_first_unused_block() const
void set_revision(uint4 revision_)
void commit(const GlassTable *B, uint4 block_size)
bool unpack(const char **pstart, const char *end)
Class managing a Btree table in a Glass database.
Definition: glass_table.h:432
void block_to_cursor(Glass::Cursor *C_, int j, uint4 n) const
Definition: glass_table.cc:317
bool Btree_modified
Set to true the first time the B-tree is modified.
Definition: glass_table.h:840
uint4 changed_n
the last block to be changed by an addition
Definition: glass_table.h:830
void alter()
Btree::alter(); is called when the B-tree is to be altered.
Definition: glass_table.cc:380
bool next_for_sequential(Glass::Cursor *C_, int dummy) const
~GlassTable()
Close the Btree.
void compact(uint8_t *p)
compact(p) compact the block at p by shuffling all the items up to the end.
Definition: glass_table.cc:569
GlassFreeList free_list
List of free blocks.
Definition: glass_table.h:816
int seq_count
count of the number of successive instances of purely sequential addition, starting at SEQ_START_POIN...
Definition: glass_table.h:827
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.
uint8_t * buffer
buffer of size block_size for reforming blocks
Definition: glass_table.h:813
void throw_overwritten() const
Definition: glass_table.cc:296
bool key_exists(std::string_view key) const
Check if a key exists in the Btree.
void basic_open(const RootInfo *root_info, glass_revision_number_t rev)
glass_tablesize_t item_count
keeps a count of the number of items in the B-tree.
Definition: glass_table.h:775
void delete_branch_item(int j)
GlassTable::delete_branch_item(j, repeatedly) is (almost) the converse of add_branch_item.
bool faked_root_block
true if the root block is faked (not written to disk).
Definition: glass_table.h:787
void commit(glass_revision_number_t revision, RootInfo *root_info)
Commit any outstanding changes to the table.
int changed_c
directory offset corresponding to last block to be changed by an addition
Definition: glass_table.h:834
bool next_default(Glass::Cursor *C_, int j) const
uint4 root
the root block of the B-tree
Definition: glass_table.h:807
void form_key(std::string_view key) const
void split_root(uint4 split_n)
Btree needs to gain a new level to insert more items: so split root block and construct a new one.
Definition: glass_table.cc:609
uint8_t * split_p
Buffer used when splitting a block.
Definition: glass_table.h:902
int add_kt(bool found)
add_kt(found) adds the item (key-tag pair) at B->kt into the B-tree, using cursor C.
void do_open_to_read(const RootInfo *root_info, glass_revision_number_t rev)
Perform the opening operation to read.
int level
number of levels, counting from 0
Definition: glass_table.h:804
bool exists() const
Determine whether the btree exists on disk.
void enter_key_above_branch(int j, Glass::BItem newitem)
enter_key_above_branch(j, newkey) is called after a branch block split.
Definition: glass_table.cc:712
void flush_db()
Flush any outstanding changes to the DB file of the table.
bool prev_default(Glass::Cursor *C_, int j) const
bool writable
Set to true when the database is opened to write.
Definition: glass_table.h:846
void open(int flags_, const RootInfo &root_info, glass_revision_number_t rev)
Open the btree.
int delete_kt()
GlassTable(const GlassTable &)
Copying not allowed.
void enter_key_above_leaf(Glass::LeafItem previtem, Glass::LeafItem newitem)
enter_key_above_leaf(previtem, newitem) is called after a leaf block split.
Definition: glass_table.cc:656
bool find(Glass::Cursor *) const
find(C_) searches for the key of B->kt in the B-tree.
Definition: glass_table.cc:536
bool read_tag(Glass::Cursor *C_, std::string *tag, bool keep_compressed) const
Read the tag value for the key pointed to by cursor C_.
void write_block(uint4 n, const uint8_t *p, bool appending=false) const
write_block(n, p, appending) writes block n in the DB file from address p.
Definition: glass_table.cc:198
int mid_point(uint8_t *p) const
mid_point(p) finds the directory entry in c that determines the approximate mid point of the data in ...
Definition: glass_table.cc:743
void add_leaf_item(Glass::LeafItem kt)
GlassTable::add_leaf_item(kt_) adds item kt_ to the leaf block.
Definition: glass_table.cc:856
void read_block(uint4 n, uint8_t *p) const
read_block(n, p) reads block n of the DB file to address p.
Definition: glass_table.cc:167
unsigned int block_size
block size of the B tree in bytes
Definition: glass_table.h:778
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.
bool cursor_created_since_last_modification
Flag for tracking when cursors need to rebuild.
Definition: glass_table.h:849
void set_full_compaction(bool parity)
unsigned long cursor_version
Version count for tracking when cursors need to rebuild.
Definition: glass_table.h:852
void add_item_to_branch(uint8_t *p, Glass::BItem kt, int c)
add_item_to_branch(p, kt_, c) adds item kt_ to the branch block at p.
Definition: glass_table.cc:820
std::string name
The path name of the B tree.
Definition: glass_table.h:822
bool lazy
If true, don't create the table until it's needed.
Definition: glass_table.h:910
bool single_file() const
Definition: glass_table.h:860
void add_branch_item(Glass::BItem kt, int j)
GlassTable::add_item(kt_, j) adds item kt_ to the block at cursor level C[j].
Definition: glass_table.cc:951
static uint4 block_given_by(const uint8_t *p, int c)
block_given_by(p, c) finds the item at block address p, directory offset c, and returns its tag value...
static int find_in_branch(const uint8_t *p, Glass::LeafItem item, int c)
Definition: glass_table.cc:515
void close(bool permanent=false)
Close the Btree.
void delete_leaf_item(bool repeatedly)
GlassTable::delete_leaf_item(repeatedly) is (almost) the converse of add_leaf_item.
Glass::Cursor C[GLASS_BTREE_CURSOR_LEVELS]
Definition: glass_table.h:895
bool del(std::string_view key)
Delete an entry from the table.
int handle
File descriptor of the table.
Definition: glass_table.h:801
void cancel(const RootInfo &root_info, glass_revision_number_t rev)
Cancel any outstanding changes.
Glass::LeafItem_wr kt
buffer of size block_size for making up key-tag items
Definition: glass_table.h:810
bool prev_for_sequential(Glass::Cursor *C_, int dummy) const
void add_item_to_leaf(uint8_t *p, Glass::LeafItem kt, int c)
add_item_to_leaf(p, kt_, c) adds item kt_ to the leaf block at p.
Definition: glass_table.cc:780
void do_open_to_write(const RootInfo *root_info, glass_revision_number_t rev=0)
Perform the opening operation to write.
static void throw_database_closed()
Throw an exception indicating that the database is closed.
uint4 compress_min
Minimum size tag to try compressing (0 for no compression).
Definition: glass_table.h:905
bool sequential
true iff the data has been written in a single write in sequential order.
Definition: glass_table.h:792
int flags
Flags like DB_NO_SYNC and DB_DANGEROUS.
Definition: glass_table.h:781
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 read_root()
static int find_in_leaf(const uint8_t *p, Glass::LeafItem item, int c, bool &exact)
find_in_leaf(p, key, c, exact) searches for the key in the leaf block at p.
Definition: glass_table.cc:424
glass_revision_number_t revision_number
revision number of the opened B-tree.
Definition: glass_table.h:772
Key key() const
Definition: glass_table.h:329
uint4 block_given_by() const
Get this item's tag as a block number (this block should not be at level 0).
Definition: glass_table.h:333
T get_address() const
Definition: glass_table.h:324
int size() const
SIZE in diagram above.
Definition: glass_table.h:326
void set_truncated_key_and_block(Key newkey, int new_comp, int truncate_size, uint4 n)
Definition: glass_table.h:370
void set_key_and_block(Key newkey, uint4 n)
Definition: glass_table.h:362
void form_null_key(uint4 n)
Form an item with a null key and with block number n in the tag.
Definition: glass_table.h:392
void set_block_given_by(uint4 n)
Set this item's tag to point to block n (this block should not be at level 0).
Definition: glass_table.h:387
static void setD(uint8_t *q, int c, int x)
Definition: glass_table.h:398
uint8_t * init(unsigned block_size)
Definition: glass_cursor.h:55
bool rewrite
true if the block is not the same as on disk, and so needs rewriting
Definition: glass_cursor.h:138
void destroy()
Definition: glass_cursor.h:84
int c
offset in the block's directory
Definition: glass_cursor.h:135
uint4 get_n() const
Get the block number.
Definition: glass_cursor.h:102
const uint8_t * clone(const Cursor &o)
Definition: glass_cursor.h:69
const uint8_t * get_p() const
Get pointer to block.
Definition: glass_cursor.h:117
void set_n(uint4 n)
Definition: glass_cursor.h:107
int length() const
Definition: glass_table.h:151
int size() const
SIZE in diagram above.
Definition: glass_table.h:184
bool last_component() const
Definition: glass_table.h:189
void append_chunk(std::string *tag) const
Definition: glass_table.h:195
int component_of() const
Definition: glass_table.h:190
bool get_compressed() const
Definition: glass_table.h:187
void init(T p_, int c)
Definition: glass_table.h:179
bool decompress_chunk(CompressionStream &comp_stream, string &tag) const
Definition: glass_table.h:204
static void setD(uint8_t *q, int c, int x)
Definition: glass_table.h:285
void set_num_entries(glass_tablesize_t n)
Definition: glass_version.h:73
const std::string & get_free_list() const
Definition: glass_version.h:70
void init(unsigned blocksize_, uint4 compress_min_)
glass_tablesize_t get_num_entries() const
Definition: glass_version.h:61
void set_blocksize(unsigned b)
Definition: glass_version.h:77
bool get_sequential() const
Definition: glass_version.h:63
void set_level(int level_)
Definition: glass_version.h:72
unsigned get_blocksize() const
Definition: glass_version.h:64
void set_sequential(bool f)
Definition: glass_version.h:75
void set_free_list(const std::string &s)
Definition: glass_version.h:82
int get_level() const
Definition: glass_version.h:60
uint4 get_compress_min() const
Definition: glass_version.h:69
void set_root_is_fake(bool f)
Definition: glass_version.h:74
glass_block_t get_root() const
Definition: glass_version.h:59
bool get_root_is_fake() const
Definition: glass_version.h:62
void set_root(glass_block_t root_)
Definition: glass_version.h:76
Indicates an attempt to access a closed database.
Definition: error.h:1085
DatabaseCorruptError indicates database corruption was detected.
Definition: error.h:397
DatabaseError indicates some sort of database related error.
Definition: error.h:355
DatabaseModifiedError indicates a database was modified.
Definition: error.h:527
DatabaseOpeningError indicates failure to open a database.
Definition: error.h:569
InvalidOperationError indicates the API was used in an invalid way.
Definition: error.h:271
UnimplementedError indicates an attempt to use an unimplemented feature.
Definition: error.h:313
#define rare(COND)
Definition: config.h:607
Constants in the Xapian namespace.
PositionList * p
Debug logging macros.
#define RETURN(...)
Definition: debuglog.h:484
#define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS)
Definition: debuglog.h:478
#define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS)
Definition: debuglog.h:480
#define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS)
Definition: debuglog.h:482
#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.
Utility functions for testing files.
bool file_exists(const char *path)
Test if a file exists.
Definition: filetests.h:40
Glass changesets.
#define DIR_START
Definition: glass_cursor.cc:52
Interface to Btree cursors.
Definitions, types, etc for use inside glass.
#define GLASS_BTREE_CURSOR_LEVELS
Allow for this many levels in the B-tree.
Definition: glass_defs.h:43
#define GLASS_MIN_BLOCKSIZE
Minimum B-tree block size.
Definition: glass_defs.h:33
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
Glass freelist.
static void throw_corrupt(const char *message)
Definition: glass_table.cc:64
#define SEQ_START_POINT
Flip to sequential addition block-splitting after this number of observed sequential additions (in ne...
Definition: glass_table.cc:153
static uint8_t * zeroed_new(size_t size)
Definition: glass_table.cc:92
#define BYTE_PAIR_RANGE
Definition: glass_table.cc:163
int find_in_branch_(const uint8_t *p, ITEM item, int c)
Definition: glass_table.cc:475
Btree implementation.
#define GLASS_BTREE_MAX_KEY_LEN
The largest possible value of a key_len.
Definition: glass_table.h:56
GlassVersion class.
#define true
Definition: header.h:8
#define false
Definition: header.h:9
const uint4 BLK_UNUSED
Definition: honey_table.h:73
uint32_t uint4
Definition: internaltypes.h:31
void io_read_block(int fd, char *p, size_t n, off_t b, off_t o)
Read block b size n bytes into buffer p from file descriptor fd, offset o.
Definition: io_utils.cc:432
int io_open_block_wr(const char *filename, bool anew)
Open a block-based file for writing.
Definition: io_utils.cc:192
bool io_unlink(const std::string &filename)
Delete a file.
Definition: io_utils.cc:56
void io_write_block(int fd, const char *p, size_t n, off_t b, off_t o)
Write block b size n bytes from buffer p to file descriptor fd, offset o.
Definition: io_utils.cc:507
Wrappers for low-level POSIX I/O routines.
bool io_readahead_block(int, size_t, off_t, off_t=0)
Readahead block b size n bytes from file descriptor fd.
Definition: io_utils.h:190
int io_open_block_rd(const char *filename)
Open a block-based file for reading.
Definition: io_utils.h:38
int DIR_END(const uint8_t *b)
Definition: glass_table.h:113
const int LEVEL_FREELIST
Freelist blocks have their level set to LEVEL_FREELIST.
Definition: glass_table.h:138
void SET_DIR_END(uint8_t *b, int x)
Definition: glass_table.h:123
int compare(ITEM1 a, ITEM2 b)
Compare two items by their keys.
Definition: glass_table.h:941
int TOTAL_FREE(const uint8_t *b)
Definition: glass_table.h:112
const int DIR_START
Definition: glass_table.h:114
void SET_LEVEL(uint8_t *b, int x)
Definition: glass_table.h:117
int GET_LEVEL(const uint8_t *b)
Definition: glass_table.h:110
@ 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
void SET_REVISION(uint8_t *b, uint4 rev)
Definition: glass_table.h:116
const int I2
Definition: glass_table.h:72
int MAX_FREE(const uint8_t *b)
Definition: glass_table.h:111
const int D2
Definition: glass_table.h:73
const size_t BLOCK_CAPACITY
Even for items of at maximum size, it must be possible to get this number of items in a block.
Definition: glass_table.h:49
const int BYTES_PER_BLOCK_NUMBER
Definition: glass_table.h:59
uint4 REVISION(const uint8_t *b)
Definition: glass_table.h:109
const int X2
Definition: glass_table.h:74
void SET_TOTAL_FREE(uint8_t *b, int x)
Definition: glass_table.h:122
void SET_MAX_FREE(uint8_t *b, int x)
Definition: glass_table.h:121
const int K1
Definition: glass_table.h:71
string str(int value)
Convert int to std::string.
Definition: str.cc:91
int revision()
Report the revision of the library which the program is linked with.
Definition: xapian.h:146
XAPIAN_REVISION_TYPE rev
Revision number of a database.
Definition: types.h:108
const int DB_DANGEROUS
Update the database in-place.
Definition: constants.h:102
Various assertion macros.
#define AssertEq(A, B)
Definition: omassert.h:124
#define AssertEqParanoid(A, B)
Definition: omassert.h:131
#define AssertRel(A, REL, B)
Definition: omassert.h:123
#define Assert(COND)
Definition: omassert.h:122
Pack types into strings and unpack them again.
void pack_uint(std::string &s, U value)
Append an encoded unsigned integer to a string.
Definition: pack.h:315
Provides wrappers with POSIXy semantics.
#define C(X)
Convert types to std::string.
Various handy string-related helpers.
#define STRINGIZE(X)
The STRINGIZE macro converts its parameter into a string constant.
Definition: stringutils.h:41
Definition: pretty.h:48
Definition: header.h:215
functions for reading and writing different width words