xapian-core  1.4.31
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, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24 
25 #include <config.h>
26 
27 #include "glass_table.h"
28 
29 #include <xapian/error.h>
30 
31 #include "omassert.h"
32 #include "posixy_wrapper.h"
33 #include "str.h"
34 #include "stringutils.h" // For STRINGIZE().
35 
36 #include <sys/types.h>
37 
38 #include <cerrno>
39 #include <cstring> /* for memmove */
40 #include <climits> /* for CHAR_BIT */
41 
42 #include "glass_freelist.h"
43 #include "glass_changes.h"
44 #include "glass_cursor.h"
45 #include "glass_defs.h"
46 #include "glass_version.h"
47 
48 #include "debuglog.h"
49 #include "filetests.h"
50 #include "io_utils.h"
51 #include "pack.h"
52 #include "wordaccess.h"
53 
54 #include <algorithm> // for std::min()
55 #include <string>
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::set_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  set_overwritten();
344  return;
345  }
346  }
347 
348  if (rare(j != GET_LEVEL(p))) {
349  string msg = "Expected block ";
350  msg += str(n);
351  msg += " to be level ";
352  msg += str(j);
353  msg += ", not ";
354  msg += str(GET_LEVEL(p));
355  throw Xapian::DatabaseCorruptError(msg);
356  }
357 }
358 
380 void
382 {
383  LOGCALL_VOID(DB, "GlassTable::alter", NO_ARGS);
384  Assert(writable);
385  if (flags & Xapian::DB_DANGEROUS) {
386  C[0].rewrite = true;
387  return;
388  }
389  int j = 0;
390  while (true) {
391  if (C[j].rewrite) return; /* all new, so return */
392  C[j].rewrite = true;
393 
394  glass_revision_number_t rev = REVISION(C[j].get_p());
395  if (rev == revision_number + 1) {
396  return;
397  }
398  Assert(rev < revision_number + 1);
399  uint4 n = C[j].get_n();
400  free_list.mark_block_unused(this, block_size, n);
401  SET_REVISION(C[j].get_modifiable_p(block_size), revision_number + 1);
402  n = free_list.get_block(this, block_size);
403  C[j].set_n(n);
404 
405  if (j == level) return;
406  j++;
407  BItem_wr(C[j].get_modifiable_p(block_size), C[j].c).set_block_given_by(n);
408  }
409 }
410 
424 int
425 GlassTable::find_in_leaf(const uint8_t * p, LeafItem item, int c, bool& exact)
426 {
427  LOGCALL_STATIC(DB, int, "GlassTable::find_in_leaf", (const void*)p | (const void *)item.get_address() | c | Literal("bool&"));
428  // c should be odd (either -1, or an even offset from DIR_START).
429  Assert((c & 1) == 1);
430  int i = DIR_START;
431  i -= D2;
432  if (c != -1) {
433  AssertRel(i,<=,c);
434  }
435  int j = DIR_END(p);
436 
437  if (c != -1) {
438  if (c < j && i < c) {
439  int r = compare(LeafItem(p, c), item);
440  if (r == 0) {
441  exact = true;
442  return c;
443  }
444  if (r < 0) i = c;
445  }
446  c += D2;
447  if (c < j && i < c) {
448  int r = compare(item, LeafItem(p, c));
449  if (r == 0) {
450  exact = true;
451  return c;
452  }
453  if (r < 0) j = c;
454  }
455  }
456 
457  while (j - i > D2) {
458  int k = i + ((j - i) / (D2 * 2)) * D2; /* mid way */
459  int r = compare(item, LeafItem(p, k));
460  if (r < 0) {
461  j = k;
462  } else {
463  i = k;
464  if (r == 0) {
465  exact = true;
466  break;
467  }
468  }
469  }
470  AssertRel(DIR_START - D2,<=,i);
471  AssertRel(i,<,DIR_END(p));
472  RETURN(i);
473 }
474 
475 template<typename ITEM> int
476 find_in_branch_(const uint8_t * p, ITEM item, int c)
477 {
478  // c should be odd (either -1, or an even offset from DIR_START).
479  Assert((c & 1) == 1);
480  int i = DIR_START;
481  if (c != -1) {
482  AssertRel(i,<=,c);
483  }
484  int j = DIR_END(p);
485 
486  if (c != -1) {
487  if (c < j && i < c) {
488  int r = compare(BItem(p, c), item);
489  if (r == 0) return c;
490  if (r < 0) i = c;
491  }
492  c += D2;
493  if (c < j && i < c) {
494  int r = compare(item, BItem(p, c));
495  if (r == 0) return c;
496  if (r < 0) j = c;
497  }
498  }
499 
500  while (j - i > D2) {
501  int k = i + ((j - i) / (D2 * 2)) * D2; /* mid way */
502  int r = compare(item, BItem(p, k));
503  if (r < 0) {
504  j = k;
505  } else {
506  i = k;
507  if (r == 0) break;
508  }
509  }
510  AssertRel(DIR_START,<=,i);
511  AssertRel(i,<,DIR_END(p));
512  return i;
513 }
514 
515 int
516 GlassTable::find_in_branch(const uint8_t * p, LeafItem item, int c)
517 {
518  LOGCALL_STATIC(DB, int, "GlassTable::find_in_branch", (const void*)p | (const void *)item.get_address() | c);
519  RETURN(find_in_branch_(p, item, c));
520 }
521 
522 int
523 GlassTable::find_in_branch(const uint8_t * p, BItem item, int c)
524 {
525  LOGCALL_STATIC(DB, int, "GlassTable::find_in_branch", (const void*)p | (const void *)item.get_address() | c);
526  RETURN(find_in_branch_(p, item, c));
527 }
528 
536 bool
538 {
539  LOGCALL(DB, bool, "GlassTable::find", (void*)C_);
540  // Note: the parameter is needed when we're called by GlassCursor
541  const uint8_t * p;
542  int c;
543  for (int j = level; j > 0; --j) {
544  p = C_[j].get_p();
545  c = find_in_branch(p, kt, C_[j].c);
546 #ifdef BTREE_DEBUG_FULL
547  printf("Block in GlassTable:find - code position 1");
548  report_block_full(j, C_[j].get_n(), p);
549 #endif /* BTREE_DEBUG_FULL */
550  C_[j].c = c;
551  block_to_cursor(C_, j - 1, BItem(p, c).block_given_by());
552  }
553  p = C_[0].get_p();
554  bool exact = false;
555  c = find_in_leaf(p, kt, C_[0].c, exact);
556 #ifdef BTREE_DEBUG_FULL
557  printf("Block in GlassTable:find - code position 2");
558  report_block_full(0, C_[0].get_n(), p);
559 #endif /* BTREE_DEBUG_FULL */
560  C_[0].c = c;
561  RETURN(exact);
562 }
563 
569 void
571 {
572  LOGCALL_VOID(DB, "GlassTable::compact", (void*)p);
573  Assert(p != buffer);
574  Assert(writable);
575  int e = block_size;
576  uint8_t * b = buffer;
577  int dir_end = DIR_END(p);
578  if (GET_LEVEL(p) == 0) {
579  // Leaf.
580  for (int c = DIR_START; c < dir_end; c += D2) {
581  LeafItem item(p, c);
582  int l = item.size();
583  e -= l;
584  if (e < 0) throw_corrupt("Leaf item size extends outside block");
585  memcpy(b + e, item.get_address(), l);
586  LeafItem_wr::setD(p, c, e); /* reform in b */
587  }
588  } else {
589  // Branch.
590  for (int c = DIR_START; c < dir_end; c += D2) {
591  BItem item(p, c);
592  int l = item.size();
593  e -= l;
594  if (e < 0) throw_corrupt("Branch item size extends outside block");
595  memcpy(b + e, item.get_address(), l);
596  BItem_wr::setD(p, c, e); /* reform in b */
597  }
598  }
599  memcpy(p + e, b + e, block_size - e); /* copy back */
600  e -= dir_end;
601  if (e < 0) throw_corrupt("Items overlap with item pointers");
602  SET_TOTAL_FREE(p, e);
603  SET_MAX_FREE(p, e);
604 }
605 
609 void
611 {
612  LOGCALL_VOID(DB, "GlassTable::split_root", split_n);
613  /* gain a level */
614  ++level;
615 
616  /* check level overflow - this isn't something that should ever happen
617  * but deserves more than an Assert()... */
618  if (level == GLASS_BTREE_CURSOR_LEVELS) {
619  throw Xapian::DatabaseCorruptError("Btree has grown impossibly large ("
621  " levels)");
622  }
623 
624  uint8_t * q = C[level].init(block_size);
625  memset(q, 0, block_size);
626  C[level].c = DIR_START;
627  C[level].set_n(free_list.get_block(this, block_size));
628  C[level].rewrite = true;
629  SET_REVISION(q, revision_number + 1);
630  SET_LEVEL(q, level);
632  compact(q); /* to reset TOTAL_FREE, MAX_FREE */
633 
634  /* form a null key in b with a pointer to the old root */
635  uint8_t b[10]; /* 7 is exact */
636  BItem_wr item(b);
637  item.form_null_key(split_n);
638  add_branch_item(item, level);
639 }
640 
656 void
658 {
659  LOGCALL_VOID(DB, "GlassTable::enter_key_above_leaf", Literal("previtem") | Literal("newitem"));
660  Assert(writable);
661  Assert(compare(previtem, newitem) < 0);
662 
663  Key prevkey = previtem.key();
664  Key newkey = newitem.key();
665  int new_comp = newitem.component_of();
666 
667  uint4 blocknumber = C[0].get_n();
668 
669  // FIXME update to use Key
670  // Keys are truncated here: but don't truncate the count at the end away.
671  const int newkey_len = newkey.length();
672  AssertRel(newkey_len,>,0);
673 
674  // Truncate the key to the minimal key which differs from prevkey,
675  // the preceding key in the block.
676  int i = 0;
677  const int min_len = min(newkey_len, prevkey.length());
678  while (i < min_len && prevkey[i] == newkey[i]) {
679  i++;
680  }
681 
682  // Want one byte of difference.
683  if (i < newkey_len) i++;
684 
685  // Enough space for a branch item with maximum length key.
686  uint8_t b[BYTES_PER_BLOCK_NUMBER + K1 + 255 + X2];
687  BItem_wr item(b);
688  AssertRel(i, <=, 255);
689  item.set_truncated_key_and_block(newkey, new_comp, i, blocknumber);
690 
691  // The split block gets inserted into the parent after the pointer to the
692  // current child.
693  AssertEq(C[1].c, find_in_branch(C[1].get_p(), item, C[1].c));
694  C[1].c += D2;
695  C[1].rewrite = true; /* a subtle point: this *is* required. */
696  add_branch_item(item, 1);
697 }
698 
712 void
714 {
715  LOGCALL_VOID(DB, "GlassTable::enter_key_above_branch", j | Literal("newitem"));
716  Assert(writable);
717  AssertRel(j,>,1);
718 
719  /* Can't truncate between branch levels, since the separated keys
720  * are in at the leaf level, and truncating again will change the
721  * branch point.
722  */
723 
724  uint4 blocknumber = C[j - 1].get_n();
725 
726  // Enough space for a branch item with maximum length key.
727  uint8_t b[BYTES_PER_BLOCK_NUMBER + K1 + 255 + X2];
728  BItem_wr item(b);
729  item.set_key_and_block(newitem.key(), blocknumber);
730 
731  // The split block gets inserted into the parent after the pointer to the
732  // current child.
733  AssertEq(C[j].c, find_in_branch(C[j].get_p(), item, C[j].c));
734  C[j].c += D2;
735  C[j].rewrite = true; /* a subtle point: this *is* required. */
736  add_branch_item(item, j);
737 }
738 
743 int
744 GlassTable::mid_point(uint8_t * p) const
745 {
746  LOGCALL(DB, int, "GlassTable::mid_point", (void*)p);
747  int n = 0;
748  int dir_end = DIR_END(p);
749  int size = block_size - TOTAL_FREE(p) - dir_end;
750  for (int c = DIR_START; c < dir_end; c += D2) {
751  int l;
752  if (GET_LEVEL(p) == 0) {
753  l = LeafItem(p, c).size();
754  } else {
755  l = BItem(p, c).size();
756  }
757  n += 2 * l;
758  if (n >= size) {
759  if (l < n - size) RETURN(c);
760  RETURN(c + D2);
761  }
762  }
763 
764  /* This shouldn't happen, as the sum of the item sizes should be the same
765  * as the value calculated in size, so assert but return a sane value just
766  * in case. */
767  Assert(false);
768  RETURN(dir_end);
769 }
770 
780 void
781 GlassTable::add_item_to_leaf(uint8_t * p, LeafItem kt_, int c)
782 {
783  LOGCALL_VOID(DB, "GlassTable::add_item_to_leaf", (void*)p | Literal("kt_") | c);
784  Assert(writable);
785  int dir_end = DIR_END(p);
786  int kt_len = kt_.size();
787  int needed = kt_len + D2;
788  int new_total = TOTAL_FREE(p) - needed;
789  int new_max = MAX_FREE(p) - needed;
790 
791  Assert(new_total >= 0);
792 
793  AssertRel(MAX_FREE(p),>=,needed);
794 
795  AssertRel(DIR_START,<=,c);
796  AssertRel(c,<=,dir_end);
797 
798  memmove(p + c + D2, p + c, dir_end - c);
799  dir_end += D2;
800  SET_DIR_END(p, dir_end);
801 
802  int o = dir_end + new_max;
803  LeafItem_wr::setD(p, c, o);
804  memmove(p + o, kt_.get_address(), kt_len);
805 
806  SET_MAX_FREE(p, new_max);
807 
808  SET_TOTAL_FREE(p, new_total);
809 }
810 
820 void
821 GlassTable::add_item_to_branch(uint8_t * p, BItem kt_, int c)
822 {
823  LOGCALL_VOID(DB, "GlassTable::add_item_to_branch", (void*)p | Literal("kt_") | c);
824  Assert(writable);
825  int dir_end = DIR_END(p);
826  int kt_len = kt_.size();
827  int needed = kt_len + D2;
828  int new_total = TOTAL_FREE(p) - needed;
829  int new_max = MAX_FREE(p) - needed;
830 
831  Assert(new_total >= 0);
832 
833  AssertRel(MAX_FREE(p),>=,needed);
834 
835  AssertRel(DIR_START,<=,c);
836  AssertRel(c,<=,dir_end);
837 
838  memmove(p + c + D2, p + c, dir_end - c);
839  dir_end += D2;
840  SET_DIR_END(p, dir_end);
841 
842  int o = dir_end + new_max;
843  BItem_wr::setD(p, c, o);
844  memmove(p + o, kt_.get_address(), kt_len);
845 
846  SET_MAX_FREE(p, new_max);
847 
848  SET_TOTAL_FREE(p, new_total);
849 }
850 
856 void
858 {
859  LOGCALL_VOID(DB, "GlassTable::add_leaf_item", Literal("kt_"));
860  Assert(writable);
861  uint8_t * p = C[0].get_modifiable_p(block_size);
862  int c = C[0].c;
863  uint4 n;
864 
865  int needed = kt_.size() + D2;
866  if (TOTAL_FREE(p) < needed) {
867  int m;
868  // Prepare to split p. After splitting, the block is in two halves, the
869  // lower half is split_p, the upper half p again. add_to_upper_half
870  // becomes true when the item gets added to p, false when it gets added
871  // to split_p.
872 
873  if (seq_count < 0) {
874  // If we're not in sequential mode, we split at the mid point
875  // of the node.
876  m = mid_point(p);
877  } else {
878  // During sequential addition, split at the insert point
879  AssertRel(c,>=,DIR_START);
880  m = c;
881  }
882 
883  uint4 split_n = C[0].get_n();
884  C[0].set_n(free_list.get_block(this, block_size));
885 
886  memcpy(split_p, p, block_size); // replicate the whole block in split_p
887  SET_DIR_END(split_p, m);
888  compact(split_p); /* to reset TOTAL_FREE, MAX_FREE */
889 
890  {
891  int residue = DIR_END(p) - m;
892  int new_dir_end = DIR_START + residue;
893  memmove(p + DIR_START, p + m, residue);
894  SET_DIR_END(p, new_dir_end);
895  }
896 
897  compact(p); /* to reset TOTAL_FREE, MAX_FREE */
898 
899  bool add_to_upper_half;
900  if (seq_count < 0) {
901  add_to_upper_half = (c >= m);
902  } else {
903  // And add item to lower half if split_p has room, otherwise upper
904  // half
905  add_to_upper_half = (TOTAL_FREE(split_p) < needed);
906  }
907 
908  if (add_to_upper_half) {
909  c -= (m - DIR_START);
910  Assert(seq_count < 0 || c <= DIR_START + D2);
911  Assert(c >= DIR_START);
912  Assert(c <= DIR_END(p));
913  add_item_to_leaf(p, kt_, c);
914  n = C[0].get_n();
915  } else {
916  Assert(c >= DIR_START);
917  Assert(c <= DIR_END(split_p));
918  add_item_to_leaf(split_p, kt_, c);
919  n = split_n;
920  }
921  write_block(split_n, split_p);
922 
923  // Check if we're splitting the root block.
924  if (0 == level) split_root(split_n);
925 
926  /* Enter a separating key at level 1 between */
927  /* the last key of block split_p, and the first key of block p */
928  enter_key_above_leaf(LeafItem(split_p, DIR_END(split_p) - D2),
929  LeafItem(p, DIR_START));
930  } else {
931  AssertRel(TOTAL_FREE(p),>=,needed);
932 
933  if (MAX_FREE(p) < needed) {
934  compact(p);
935  AssertRel(MAX_FREE(p),>=,needed);
936  }
937 
938  add_item_to_leaf(p, kt_, c);
939  n = C[0].get_n();
940  }
941 
942  changed_n = n;
943  changed_c = c;
944 }
945 
951 void
953 {
954  LOGCALL_VOID(DB, "GlassTable::add_branch_item", Literal("kt_") | j);
955  Assert(writable);
956  uint8_t * p = C[j].get_modifiable_p(block_size);
957  int c = C[j].c;
958 
959  int needed = kt_.size() + D2;
960  if (TOTAL_FREE(p) < needed) {
961  int m;
962  // Prepare to split p. After splitting, the block is in two halves, the
963  // lower half is split_p, the upper half p again. add_to_upper_half
964  // becomes true when the item gets added to p, false when it gets added
965  // to split_p.
966 
967  if (seq_count < 0) {
968  // If we're not in sequential mode, we split at the mid point
969  // of the node.
970  m = mid_point(p);
971  } else {
972  // During sequential addition, split at the insert point
973  AssertRel(c,>=,DIR_START);
974  m = c;
975  }
976 
977  uint4 split_n = C[j].get_n();
978  C[j].set_n(free_list.get_block(this, block_size));
979 
980  memcpy(split_p, p, block_size); // replicate the whole block in split_p
981  SET_DIR_END(split_p, m);
982  compact(split_p); /* to reset TOTAL_FREE, MAX_FREE */
983 
984  {
985  int residue = DIR_END(p) - m;
986  int new_dir_end = DIR_START + residue;
987  memmove(p + DIR_START, p + m, residue);
988  SET_DIR_END(p, new_dir_end);
989  }
990 
991  compact(p); /* to reset TOTAL_FREE, MAX_FREE */
992 
993  bool add_to_upper_half;
994  if (seq_count < 0) {
995  add_to_upper_half = (c >= m);
996  } else {
997  // And add item to lower half if split_p has room, otherwise upper
998  // half
999  add_to_upper_half = (TOTAL_FREE(split_p) < needed);
1000  }
1001 
1002  if (add_to_upper_half) {
1003  c -= (m - DIR_START);
1004  Assert(seq_count < 0 || c <= DIR_START + D2);
1005  Assert(c >= DIR_START);
1006  Assert(c <= DIR_END(p));
1007  add_item_to_branch(p, kt_, c);
1008  } else {
1009  Assert(c >= DIR_START);
1010  Assert(c <= DIR_END(split_p));
1011  add_item_to_branch(split_p, kt_, c);
1012  }
1013  write_block(split_n, split_p);
1014 
1015  // Check if we're splitting the root block.
1016  if (j == level) split_root(split_n);
1017 
1018  /* Enter a separating key at level j + 1 between */
1019  /* the last key of block split_p, and the first key of block p */
1020  enter_key_above_branch(j + 1, BItem(p, DIR_START));
1021 
1022  // In branch levels, we can make the first key of block p null and
1023  // save a bit of disk space. Other redundant keys will still creep
1024  // in though.
1025  BItem_wr item(p, DIR_START);
1026  int new_total_free = TOTAL_FREE(p) + item.key().length();
1027  item.form_null_key(item.block_given_by());
1028  SET_TOTAL_FREE(p, new_total_free);
1029  } else {
1030  AssertRel(TOTAL_FREE(p),>=,needed);
1031 
1032  if (MAX_FREE(p) < needed) {
1033  compact(p);
1034  AssertRel(MAX_FREE(p),>=,needed);
1035  }
1036 
1037  add_item_to_branch(p, kt_, c);
1038  }
1039 }
1040 
1048 void
1050 {
1051  LOGCALL_VOID(DB, "GlassTable::delete_leaf_item", repeatedly);
1052  Assert(writable);
1053  uint8_t * p = C[0].get_modifiable_p(block_size);
1054  int c = C[0].c;
1055  AssertRel(DIR_START,<=,c);
1056  AssertRel(c,<,DIR_END(p));
1057  int kt_len = LeafItem(p, c).size(); /* size of the item to be deleted */
1058  int dir_end = DIR_END(p) - D2; /* directory length will go down by 2 bytes */
1059 
1060  memmove(p + c, p + c + D2, dir_end - c);
1061  SET_DIR_END(p, dir_end);
1062  SET_MAX_FREE(p, MAX_FREE(p) + D2);
1063  SET_TOTAL_FREE(p, TOTAL_FREE(p) + kt_len + D2);
1064 
1065  if (!repeatedly) return;
1066  if (0 < level) {
1067  if (dir_end == DIR_START) {
1068  free_list.mark_block_unused(this, block_size, C[0].get_n());
1069  C[0].rewrite = false;
1070  C[0].set_n(BLK_UNUSED);
1071  C[1].rewrite = true; /* *is* necessary */
1072  delete_branch_item(1);
1073  }
1074  }
1075 }
1076 
1083 void
1085 {
1086  LOGCALL_VOID(DB, "GlassTable::delete_branch_item", j);
1087  Assert(writable);
1088  uint8_t * p = C[j].get_modifiable_p(block_size);
1089  int c = C[j].c;
1090  AssertRel(DIR_START,<=,c);
1091  AssertRel(c,<,DIR_END(p));
1092  int kt_len = BItem(p, c).size(); /* size of the item to be deleted */
1093  int dir_end = DIR_END(p) - D2; /* directory length will go down by 2 bytes */
1094 
1095  memmove(p + c, p + c + D2, dir_end - c);
1096  SET_DIR_END(p, dir_end);
1097  SET_MAX_FREE(p, MAX_FREE(p) + D2);
1098  SET_TOTAL_FREE(p, TOTAL_FREE(p) + kt_len + D2);
1099 
1100  if (j < level) {
1101  if (dir_end == DIR_START) {
1102  free_list.mark_block_unused(this, block_size, C[j].get_n());
1103  C[j].rewrite = false;
1104  C[j].set_n(BLK_UNUSED);
1105  C[j + 1].rewrite = true; /* *is* necessary */
1106  delete_branch_item(j + 1);
1107  }
1108  } else {
1109  Assert(j == level);
1110  while (dir_end == DIR_START + D2 && level > 0) {
1111  /* single item in the root block, so lose a level */
1112  uint4 new_root = BItem(C[level].get_p(), DIR_START).block_given_by();
1113  free_list.mark_block_unused(this, block_size, C[level].get_n());
1114  C[level].destroy();
1115  level--;
1116 
1117  block_to_cursor(C, level, new_root);
1118 
1119  dir_end = DIR_END(C[level].get_p()); /* prepare for the loop */
1120  }
1121  }
1122 }
1123 
1124 /* debugging aid:
1125 static addcount = 0;
1126 */
1127 
1158 int
1160 {
1161  LOGCALL(DB, int, "GlassTable::add_kt", found);
1162  Assert(writable);
1163 
1164  /*
1165  {
1166  printf("%d) %s ", addcount++, (found ? "replacing" : "adding"));
1167  print_bytes(kt[I2], kt + I2 + K1); putchar('\n');
1168  }
1169  */
1170  alter();
1171 
1172  int result = 0;
1173  if (found) { /* replacement */
1174  seq_count = SEQ_START_POINT;
1175  sequential = false;
1176 
1177  uint8_t * p = C[0].get_modifiable_p(block_size);
1178  int c = C[0].c;
1179  AssertRel(DIR_START,<=,c);
1180  AssertRel(c,<,DIR_END(p));
1181  LeafItem item(p, c);
1182  int kt_size = kt.size();
1183  int needed = kt_size - item.size();
1184 
1185  result = item.last_component() ? 2 : 1;
1186 
1187  if (needed <= 0) {
1188  /* simple replacement */
1189  memmove(const_cast<uint8_t *>(item.get_address()),
1190  kt.get_address(), kt_size);
1191  SET_TOTAL_FREE(p, TOTAL_FREE(p) - needed);
1192  } else {
1193  /* new item into the block's freespace */
1194  int new_max = MAX_FREE(p) - kt_size;
1195  if (new_max >= 0) {
1196  int o = DIR_END(p) + new_max;
1197  memmove(p + o, kt.get_address(), kt_size);
1198  LeafItem_wr::setD(p, c, o);
1199  SET_MAX_FREE(p, new_max);
1200  SET_TOTAL_FREE(p, TOTAL_FREE(p) - needed);
1201  } else {
1202  /* do it the long way */
1203  delete_leaf_item(false);
1204  add_leaf_item(kt);
1205  }
1206  }
1207  } else {
1208  /* addition */
1209  if (changed_n == C[0].get_n() && changed_c == C[0].c) {
1210  if (seq_count < 0) seq_count++;
1211  } else {
1212  seq_count = SEQ_START_POINT;
1213  sequential = false;
1214  }
1215  C[0].c += D2;
1216  add_leaf_item(kt);
1217  }
1218  RETURN(result);
1219 }
1220 
1221 /* delete_kt() corresponds to add_kt(found), but there are only
1222  two cases: if the key is not found nothing is done, and if it is
1223  found the corresponding item is deleted with delete_leaf_item.
1224 
1225  Returns:
1226  0 : nothing to delete
1227  1 : deleted kt
1228  2 : deleted kt and it was the final one
1229 */
1230 
1231 int
1233 {
1234  LOGCALL(DB, int, "GlassTable::delete_kt", NO_ARGS);
1235  Assert(writable);
1236 
1237  seq_count = SEQ_START_POINT;
1238  sequential = false;
1239 
1240  if (!find(C))
1241  return 0;
1242 
1243  int result = LeafItem(C[0].get_p(), C[0].c).last_component() ? 2 : 1;
1244  alter();
1245  delete_leaf_item(true);
1246 
1247  RETURN(result);
1248 }
1249 
1250 /* GlassTable::form_key(key) treats address kt as an item holder and fills in
1251 the key part:
1252 
1253  (I) K key c (C tag)
1254 
1255 The bracketed parts are left blank. The key is filled in with key_len bytes and
1256 K set accordingly. c is set to 1.
1257 */
1258 
1259 void GlassTable::form_key(const string & key) const
1260 {
1261  LOGCALL_VOID(DB, "GlassTable::form_key", key);
1262  kt.form_key(key);
1263 }
1264 
1265 /* GlassTable::add(key, tag) adds the key/tag item to the
1266  B-tree, replacing any existing item with the same key.
1267 
1268  For a long tag, we end up having to add m components, of the form
1269 
1270  key 1 m tag1
1271  key 2 m tag2
1272  ...
1273  key m m tagm
1274 
1275  and tag1+tag2+...+tagm are equal to tag. These in their turn may be replacing
1276  n components of the form
1277 
1278  key 1 n TAG1
1279  key 2 n TAG2
1280  ...
1281  key n n TAGn
1282 
1283  and n may be greater than, equal to, or less than m. These cases are dealt
1284  with in the code below. If m < n for example, we end up with a series of
1285  deletions.
1286 */
1287 
1288 void
1289 GlassTable::add(const string& key, const string& tag, bool already_compressed)
1290 {
1291  LOGCALL_VOID(DB, "GlassTable::add", key | tag | already_compressed);
1292  Assert(writable);
1293 
1294  if (handle < 0) {
1295  if (handle == -2) {
1297  }
1298  RootInfo root_info;
1299  root_info.init(block_size, compress_min);
1300  do_open_to_write(&root_info);
1301  }
1302 
1303  form_key(key);
1304 
1305  const char* tag_data = tag.data();
1306  size_t tag_size = tag.size();
1307 
1308  bool compressed = false;
1309  if (already_compressed) {
1310  compressed = true;
1311  } else if (compress_min > 0 && tag_size > compress_min) {
1312  const char * res = comp_stream.compress(tag_data, &tag_size);
1313  if (res) {
1314  compressed = true;
1315  tag_data = res;
1316  }
1317  }
1318 
1319  // sort of matching kt.append_chunk(), but setting the chunk
1320  const size_t cd = kt.key().length() + K1 + I2 + X2; // offset to the tag data
1321  const size_t L = max_item_size - cd; // largest amount of tag data for any chunk
1322  size_t first_L = L + X2; // - amount for tag1 (we don't store X there)
1323  bool found = find(C);
1324  if (tag_size <= first_L) {
1325  // The whole tag clearly fits in one item, so no need to make this
1326  // complicated.
1327  first_L = tag_size;
1328  } else if (!found) {
1329  const uint8_t * p = C[0].get_p();
1330  size_t n = TOTAL_FREE(p) % (max_item_size + D2);
1331  if (n > D2 + cd) {
1332  n -= (D2 + cd);
1333  // if n >= last then fully filling this block won't produce
1334  // an extra item, so we might as well do this even if
1335  // full_compaction isn't active.
1336  //
1337  // In the full_compaction case, it turns out we shouldn't always
1338  // try to fill every last byte. Doing so can actually increase the
1339  // total space required (I believe this effect is due to longer
1340  // dividing keys being required in the index blocks). Empirically,
1341  // n >= key.size() + K appears a good criterion for K ~= 34. This
1342  // seems to save about 0.2% in total database size over always
1343  // splitting the tag. It'll also give be slightly faster retrieval
1344  // as we can avoid reading an extra block occasionally.
1345  size_t last = (tag_size - X2) % L;
1346  if (n >= last || (full_compaction && n >= key.size() + 34)) {
1347  // first_L < max_item_size + D2 - D2 - cd
1348  // Total size of first item = cd + first_L < max_item_size
1349  first_L = n + X2;
1350  }
1351  }
1352  }
1353 
1354  // There are m items to add.
1355  int m = (tag_size - first_L + L - 1) / L + 1;
1356  /* FIXME: sort out this error higher up and turn this into
1357  * an assert.
1358  */
1359  if (m >= BYTE_PAIR_RANGE) {
1360  string message = "Btree tag entry of size ";
1361  message += str(tag_size);
1362  message += " is too large to store - "
1363  "increase the block size to raise this limit";
1364  throw Xapian::UnimplementedError(message);
1365  }
1366 
1367  size_t o = 0; // Offset into the tag
1368  size_t residue = tag_size; // Bytes of the tag remaining to add in
1369  bool replacement = false; // Has there been a replacement?
1370  bool components_to_del = false; // Are there components to delete?
1371  int i;
1372  for (i = 1; i <= m; ++i) {
1373  size_t l = (i == m ? residue : (i == 1 ? first_L : L));
1374  size_t this_cd = (i == 1 ? cd - X2 : cd);
1375  Assert(this_cd + l <= block_size);
1376  Assert(o + l <= tag_size);
1377  kt.set_tag(this_cd, tag_data + o, l, compressed, i, m);
1378 
1379  o += l;
1380  residue -= l;
1381 
1382  if (i > 1) found = find(C);
1383  int result = add_kt(found);
1384  if (result) replacement = true;
1385  components_to_del = (result == 1);
1386  }
1387  AssertEq(o, tag_size);
1388  if (components_to_del) {
1389  i = m;
1390  do {
1391  kt.set_component_of(++i);
1392  } while (delete_kt() == 1);
1393  }
1394  if (!replacement) ++item_count;
1395  Btree_modified = true;
1396  if (cursor_created_since_last_modification) {
1397  cursor_created_since_last_modification = false;
1398  ++cursor_version;
1399  }
1400 }
1401 
1402 /* GlassTable::del(key) returns false if the key is not in the B-tree,
1403  otherwise deletes it and returns true.
1404 
1405  Again, this is parallel to GlassTable::add, but simpler in form.
1406 */
1407 
1408 bool
1409 GlassTable::del(const string &key)
1410 {
1411  LOGCALL(DB, bool, "GlassTable::del", key);
1412  Assert(writable);
1413 
1414  if (handle < 0) {
1415  if (handle == -2) {
1417  }
1418  RETURN(false);
1419  }
1420 
1421  // We can't delete a key which we is too long for us to store.
1422  if (key.size() > GLASS_BTREE_MAX_KEY_LEN) RETURN(false);
1423 
1424  if (key.empty()) RETURN(false);
1425  form_key(key);
1426 
1427  int r = delete_kt();
1428  if (r == 0) RETURN(false);
1429  int i = 1;
1430  while (r == 1) {
1431  kt.set_component_of(++i);
1432  r = delete_kt();
1433  }
1434 
1435  item_count--;
1436  Btree_modified = true;
1437  if (cursor_created_since_last_modification) {
1438  cursor_created_since_last_modification = false;
1439  ++cursor_version;
1440  }
1441  RETURN(true);
1442 }
1443 
1444 bool
1445 GlassTable::readahead_key(const string &key) const
1446 {
1447  LOGCALL(DB, bool, "GlassTable::readahead_key", key);
1448  Assert(!key.empty());
1449 
1450  // Three cases:
1451  //
1452  // handle == -1: Lazy table in a multi-file database which isn't yet open.
1453  //
1454  // handle == -2: Table has been closed. Since the readahead is just a
1455  // hint, we can safely ignore it for a closed table.
1456  //
1457  // handle <= -3: Lazy table in a single-file database which isn't yet
1458  // open.
1459  if (handle < 0)
1460  RETURN(false);
1461 
1462  // If the table only has one level, there are no branch blocks to preread.
1463  if (level == 0)
1464  RETURN(false);
1465 
1466  // An overlong key cannot be found.
1467  if (key.size() > GLASS_BTREE_MAX_KEY_LEN)
1468  RETURN(true);
1469 
1470  form_key(key);
1471 
1472  // We'll only readahead the first level, since descending the B-tree would
1473  // require actual reads that would likely hurt performance more than help.
1474  const uint8_t * p = C[level].get_p();
1475  int c = find_in_branch(p, kt, C[level].c);
1476  uint4 n = BItem(p, c).block_given_by();
1477  // Don't preread if it's the block we last preread or already in the
1478  // cursor.
1479  if (n != last_readahead && n != C[level - 1].get_n()) {
1480  last_readahead = n;
1481  if (!io_readahead_block(handle, block_size, n, offset))
1482  RETURN(false);
1483  }
1484  RETURN(true);
1485 }
1486 
1487 bool
1488 GlassTable::get_exact_entry(const string &key, string & tag) const
1489 {
1490  LOGCALL(DB, bool, "GlassTable::get_exact_entry", key | tag);
1491  Assert(!key.empty());
1492 
1493  if (handle < 0) {
1494  if (handle == -2) {
1496  }
1497  RETURN(false);
1498  }
1499 
1500  // An oversized key can't exist, so attempting to search for it should fail.
1501  if (key.size() > GLASS_BTREE_MAX_KEY_LEN) RETURN(false);
1502 
1503  form_key(key);
1504  if (!find(C)) RETURN(false);
1505 
1506  (void)read_tag(C, &tag, false);
1507  RETURN(true);
1508 }
1509 
1510 bool
1511 GlassTable::key_exists(const string &key) const
1512 {
1513  LOGCALL(DB, bool, "GlassTable::key_exists", key);
1514  Assert(!key.empty());
1515 
1516  // An oversized key can't exist, so attempting to search for it should fail.
1517  if (key.size() > GLASS_BTREE_MAX_KEY_LEN) RETURN(false);
1518 
1519  form_key(key);
1520  RETURN(find(C));
1521 }
1522 
1523 bool
1524 GlassTable::read_tag(Glass::Cursor * C_, string *tag, bool keep_compressed) const
1525 {
1526  LOGCALL(DB, bool, "GlassTable::read_tag", Literal("C_") | tag | keep_compressed);
1527 
1528  tag->resize(0);
1529 
1530  LeafItem item(C_[0].get_p(), C_[0].c);
1531  bool compressed = item.get_compressed();
1532  bool decompress = false;
1533  if (compressed && !keep_compressed) {
1534  comp_stream.decompress_start();
1535  decompress = true;
1536  }
1537 
1538  while (true) {
1539  bool last = item.last_component();
1540  if (decompress) {
1541  // Decompress each chunk as we read it so we don't need both the
1542  // full compressed and uncompressed tags in memory at once.
1543  bool done = item.decompress_chunk(comp_stream, *tag);
1544  if (done != last) {
1545  throw Xapian::DatabaseCorruptError(done ?
1546  "Too many chunks of compressed data" :
1547  "Too few chunks of compressed data");
1548  }
1549  } else {
1550  item.append_chunk(tag);
1551  }
1552  if (last) break;
1553  if (!next(C_, 0)) {
1554  throw Xapian::DatabaseCorruptError("Unexpected end of table when reading continuation of tag");
1555  }
1556  item.init(C_[0].get_p(), C_[0].c);
1557  }
1558  // At this point the cursor is on the last item - calling next will move
1559  // it to the next key (GlassCursor::read_tag() relies on this).
1560 
1561  RETURN(compressed && keep_compressed);
1562 }
1563 
1564 void
1566 {
1567  LOGCALL_VOID(DB, "GlassTable::set_full_compaction", parity);
1568  Assert(writable);
1569 
1570  if (parity) seq_count = 0;
1571  full_compaction = parity;
1572 }
1573 
1575  LOGCALL(DB, GlassCursor *, "GlassTable::cursor_get", NO_ARGS);
1576  if (handle < 0) {
1577  if (handle == -2) {
1579  }
1580  RETURN(NULL);
1581  }
1582  // FIXME Ick - casting away const is nasty
1583  RETURN(new GlassCursor(const_cast<GlassTable *>(this)));
1584 }
1585 
1586 /************ B-tree opening and closing ************/
1587 
1588 void
1590 {
1591  LOGCALL_VOID(DB, "GlassTable::basic_open", root_info|rev);
1592  revision_number = rev;
1593  root = root_info->get_root();
1594  level = root_info->get_level();
1595  item_count = root_info->get_num_entries();
1596  faked_root_block = root_info->get_root_is_fake();
1597  sequential = root_info->get_sequential();
1598  const string & fl_serialised = root_info->get_free_list();
1599  if (!fl_serialised.empty()) {
1600  if (!free_list.unpack(fl_serialised))
1601  throw Xapian::DatabaseCorruptError("Bad freelist metadata");
1602  } else {
1603  free_list.reset();
1604  }
1605 
1606  compress_min = root_info->get_compress_min();
1607 
1608  /* kt holds constructed items as well as keys */
1609  kt = LeafItem_wr(zeroed_new(block_size));
1610 
1611  set_max_item_size(BLOCK_CAPACITY);
1612 
1613  for (int j = 0; j <= level; ++j) {
1614  C[j].init(block_size);
1615  }
1616 
1617  read_root();
1618 
1619  if (cursor_created_since_last_modification) {
1620  cursor_created_since_last_modification = false;
1621  ++cursor_version;
1622  }
1623 }
1624 
1625 void
1627 {
1628  LOGCALL_VOID(DB, "GlassTable::read_root", NO_ARGS);
1629  if (faked_root_block) {
1630  /* root block for an unmodified database. */
1631  uint8_t * p = C[0].init(block_size);
1632  Assert(p);
1633 
1634  /* clear block - shouldn't be necessary, but is a bit nicer,
1635  * and means that the same operations should always produce
1636  * the same database. */
1637  memset(p, 0, block_size);
1638 
1639  int o = block_size - I2 - K1;
1640  LeafItem_wr(p + o).fake_root_item();
1641 
1642  LeafItem_wr::setD(p, DIR_START, o); // its directory entry
1643  SET_DIR_END(p, DIR_START + D2);// the directory size
1644 
1645  o -= (DIR_START + D2);
1646  SET_MAX_FREE(p, o);
1647  SET_TOTAL_FREE(p, o);
1648  SET_LEVEL(p, 0);
1649 
1650  if (!writable) {
1651  /* reading - revision number doesn't matter as long as
1652  * it's not greater than the current one. */
1653  SET_REVISION(p, 0);
1654  C[0].set_n(0);
1655  } else {
1656  /* writing - */
1657  SET_REVISION(p, revision_number + 1);
1658  C[0].set_n(free_list.get_block(this, block_size));
1659  C[0].rewrite = true;
1660  }
1661  } else {
1662  /* using a root block stored on disk */
1663  block_to_cursor(C, level, root);
1664 
1665  if (REVISION(C[level].get_p()) > revision_number) set_overwritten();
1666  /* although this is unlikely */
1667  }
1668 }
1669 
1670 void
1673 {
1674  LOGCALL_VOID(DB, "GlassTable::do_open_to_write", root_info|rev);
1675  if (handle == -2) {
1677  }
1678  if (handle <= -2) {
1679  // Single file database.
1680  handle = -3 - handle;
1681  } else {
1682  handle = io_open_block_wr(name + GLASS_TABLE_EXTENSION, (rev == 0));
1683  if (handle < 0) {
1684  // lazy doesn't make a lot of sense when we're creating a DB (which
1685  // is the case when rev==0), but ENOENT with O_CREAT means a parent
1686  // directory doesn't exist.
1687  if (lazy && rev && errno == ENOENT) {
1688  revision_number = rev;
1689  return;
1690  }
1691  string message((rev == 0) ? "Couldn't create " : "Couldn't open ");
1692  message += name;
1693  message += GLASS_TABLE_EXTENSION" read/write";
1694  throw Xapian::DatabaseOpeningError(message, errno);
1695  }
1696  }
1697 
1698  writable = true;
1699  basic_open(root_info, rev);
1700 
1701  split_p = new uint8_t[block_size];
1702 
1703  buffer = zeroed_new(block_size);
1704 
1705  changed_n = 0;
1706  changed_c = DIR_START;
1707  seq_count = SEQ_START_POINT;
1708 }
1709 
1710 GlassTable::GlassTable(const char * tablename_, const string & path_,
1711  bool readonly_, bool lazy_)
1712  : tablename(tablename_),
1713  revision_number(0),
1714  item_count(0),
1715  block_size(0),
1716  faked_root_block(true),
1717  sequential(true),
1718  handle(-1),
1719  level(0),
1720  root(0),
1721  kt(0),
1722  buffer(0),
1723  free_list(),
1724  name(path_),
1725  seq_count(0),
1726  changed_n(0),
1727  changed_c(0),
1728  max_item_size(0),
1729  Btree_modified(false),
1730  full_compaction(false),
1731  writable(!readonly_),
1732  cursor_created_since_last_modification(false),
1733  cursor_version(0),
1734  changes_obj(NULL),
1735  split_p(0),
1736  compress_min(0),
1737  comp_stream(Z_DEFAULT_STRATEGY),
1738  lazy(lazy_),
1739  last_readahead(BLK_UNUSED),
1740  offset(0)
1741 {
1742  LOGCALL_CTOR(DB, "GlassTable", tablename_ | path_ | readonly_ | lazy_);
1743 }
1744 
1745 GlassTable::GlassTable(const char * tablename_, int fd, off_t offset_,
1746  bool readonly_, bool lazy_)
1747  : tablename(tablename_),
1748  revision_number(0),
1749  item_count(0),
1750  block_size(0),
1751  faked_root_block(true),
1752  sequential(true),
1753  handle(-3 - fd),
1754  level(0),
1755  root(0),
1756  kt(0),
1757  buffer(0),
1758  free_list(),
1759  name(),
1760  seq_count(0),
1761  changed_n(0),
1762  changed_c(0),
1763  max_item_size(0),
1764  Btree_modified(false),
1765  full_compaction(false),
1766  writable(!readonly_),
1767  cursor_created_since_last_modification(false),
1768  cursor_version(0),
1769  changes_obj(NULL),
1770  split_p(0),
1771  compress_min(0),
1772  comp_stream(Z_DEFAULT_STRATEGY),
1773  lazy(lazy_),
1774  last_readahead(BLK_UNUSED),
1775  offset(offset_)
1776 {
1777  LOGCALL_CTOR(DB, "GlassTable", tablename_ | fd | offset_ | readonly_ | lazy_);
1778 }
1779 
1780 bool
1782  LOGCALL(DB, bool, "GlassTable::exists", NO_ARGS);
1783  // We know a single-file database exists, since we have an fd open on it!
1785 }
1786 
1787 void
1788 GlassTable::create_and_open(int flags_, const RootInfo & root_info)
1789 {
1790  LOGCALL_VOID(DB, "GlassTable::create_and_open", flags_|root_info);
1791  if (handle == -2) {
1793  }
1794  Assert(writable);
1795  close();
1796 
1797  unsigned int block_size_ = root_info.get_blocksize();
1798  Assert(block_size_ >= 2048);
1799  Assert(block_size_ <= BYTE_PAIR_RANGE);
1800  // Must be a power of two.
1801  Assert((block_size_ & (block_size_ - 1)) == 0);
1802 
1803  flags = flags_;
1804  block_size = block_size_;
1805 
1806  if (lazy) {
1807  close();
1809  compress_min = root_info.get_compress_min();
1810  } else {
1811  // FIXME: it would be good to arrange that this works such that there's
1812  // always a valid table in place if you run create_and_open() on an
1813  // existing table.
1814 
1815  do_open_to_write(&root_info);
1816  }
1817 }
1818 
1820  LOGCALL_DTOR(DB, "GlassTable");
1822 }
1823 
1824 void GlassTable::close(bool permanent) {
1825  LOGCALL_VOID(DB, "GlassTable::close", permanent);
1826 
1827  if (handle >= 0) {
1828  if (single_file()) {
1829  handle = -3 - handle;
1830  } else {
1831  // If an error occurs here, we just ignore it, since we're just
1832  // trying to free everything.
1833  (void)::close(handle);
1834  handle = -1;
1835  }
1836  }
1837 
1838  if (permanent) {
1839  handle = -2;
1840  // Don't delete the resources in the table, since they may
1841  // still be used to look up cached content.
1842  return;
1843  }
1844  for (int j = level; j >= 0; --j) {
1845  C[j].destroy();
1846  }
1847  delete [] split_p;
1848  split_p = 0;
1849 
1850  delete [] kt.get_address();
1851  kt = LeafItem_wr(0);
1852  delete [] buffer;
1853  buffer = 0;
1854 }
1855 
1856 void
1858 {
1859  LOGCALL_VOID(DB, "GlassTable::flush_db", NO_ARGS);
1860  Assert(writable);
1861  if (handle < 0) {
1862  if (handle == -2) {
1864  }
1865  return;
1866  }
1867 
1868  for (int j = level; j >= 0; --j) {
1869  if (C[j].rewrite) {
1870  write_block(C[j].get_n(), C[j].get_p());
1871  }
1872  }
1873 
1874  faked_root_block = false;
1875 }
1876 
1877 void
1879 {
1880  LOGCALL_VOID(DB, "GlassTable::commit", revision|root_info);
1881  Assert(writable);
1882 
1883  if (revision <= revision_number) {
1884  throw Xapian::DatabaseError("New revision too low");
1885  }
1886 
1887  if (handle < 0) {
1888  if (handle == -2) {
1890  }
1892  root_info->set_blocksize(block_size);
1893  root_info->set_level(0);
1894  root_info->set_num_entries(0);
1895  root_info->set_root_is_fake(true);
1896  root_info->set_sequential(true);
1897  root_info->set_root(0);
1898  return;
1899  }
1900 
1901  try {
1902  root = C[level].get_n();
1903 
1904  root_info->set_blocksize(block_size);
1905  root_info->set_level(level);
1906  root_info->set_num_entries(item_count);
1907  root_info->set_root_is_fake(faked_root_block);
1908  root_info->set_sequential(sequential);
1909  root_info->set_root(root);
1910 
1911  Btree_modified = false;
1912 
1913  for (int i = 0; i <= level; ++i) {
1914  C[i].init(block_size);
1915  }
1916 
1918  free_list.commit(this, block_size);
1919 
1920  // Save the freelist details into the root_info.
1921  string serialised;
1922  free_list.pack(serialised);
1923  root_info->set_free_list(serialised);
1924 
1926 
1927  read_root();
1928 
1929  changed_n = 0;
1930  changed_c = DIR_START;
1932  } catch (...) {
1934  throw;
1935  }
1936 }
1937 
1938 void
1940 {
1941  LOGCALL_VOID(DB, "GlassTable::cancel", root_info|rev);
1942  Assert(writable);
1943 
1944  if (handle < 0) {
1945  if (handle == -2) {
1947  }
1948  return;
1949  }
1950 
1951  // This causes problems: if (!Btree_modified) return;
1952 
1954  throw Xapian::InvalidOperationError("cancel() not supported under Xapian::DB_DANGEROUS");
1955 
1956  revision_number = rev;
1957  block_size = root_info.get_blocksize();
1958  root = root_info.get_root();
1959  level = root_info.get_level();
1960  item_count = root_info.get_num_entries();
1961  faked_root_block = root_info.get_root_is_fake();
1962  sequential = root_info.get_sequential();
1963  const string & fl_serialised = root_info.get_free_list();
1964  if (!fl_serialised.empty()) {
1965  if (!free_list.unpack(fl_serialised))
1966  throw Xapian::DatabaseCorruptError("Bad freelist metadata");
1967  } else {
1968  free_list.reset();
1969  }
1970 
1971  Btree_modified = false;
1972 
1973  for (int j = 0; j <= level; ++j) {
1974  C[j].init(block_size);
1975  C[j].rewrite = false;
1976  }
1977  read_root();
1978 
1979  changed_n = 0;
1980  changed_c = DIR_START;
1982 
1985  ++cursor_version;
1986  }
1987 }
1988 
1989 /************ B-tree reading ************/
1990 
1991 void
1994 {
1995  LOGCALL(DB, bool, "GlassTable::do_open_to_read", root_info|rev);
1996  if (handle == -2) {
1998  }
1999  if (single_file()) {
2000  handle = -3 - handle;
2001  } else {
2003  if (handle < 0) {
2004  if (lazy) {
2005  // This table is optional when reading!
2006  revision_number = rev;
2007  return;
2008  }
2009  string message("Couldn't open ");
2010  message += name;
2011  message += GLASS_TABLE_EXTENSION" to read";
2012  throw Xapian::DatabaseOpeningError(message, errno);
2013  }
2014  }
2015 
2016  basic_open(root_info, rev);
2017 
2018  read_root();
2019 }
2020 
2021 void
2022 GlassTable::open(int flags_, const RootInfo & root_info,
2024 {
2025  LOGCALL_VOID(DB, "GlassTable::open", flags_|root_info|rev);
2026  close();
2027 
2028  flags = flags_;
2029  block_size = root_info.get_blocksize();
2030  root = root_info.get_root();
2031 
2032  if (!writable) {
2033  do_open_to_read(&root_info, rev);
2034  return;
2035  }
2036 
2037  do_open_to_write(&root_info, rev);
2038 }
2039 
2040 bool
2042 {
2043  LOGCALL(DB, bool, "GlassTable::prev_for_sequential", Literal("C_") | Literal("/*dummy*/"));
2044  int c = C_[0].c;
2045  AssertRel(DIR_START,<=,c);
2046  AssertRel(c,<,DIR_END(C_[0].get_p()));
2047  if (c == DIR_START) {
2048  uint4 n = C_[0].get_n();
2049  const uint8_t * p;
2050  while (true) {
2051  if (n == 0) RETURN(false);
2052  n--;
2053  if (n == C[0].get_n()) {
2054  // Block is a leaf block in the built-in cursor (potentially in
2055  // modified form if the table is writable).
2056  p = C_[0].clone(C[0]);
2057  } else {
2058  if (writable) {
2059  // Blocks in the built-in cursor may not have been written
2060  // to disk yet, so we have to check that the block number
2061  // isn't in the built-in cursor or we'll read an
2062  // uninitialised block (for which GET_LEVEL(p) will
2063  // probably return 0).
2064  int j;
2065  for (j = 1; j <= level; ++j) {
2066  if (n == C[j].get_n()) break;
2067  }
2068  if (j <= level) continue;
2069  }
2070 
2071  // Block isn't in the built-in cursor, so the form on disk
2072  // is valid, so read it to check if it's the next level 0
2073  // block.
2074  uint8_t * q = C_[0].init(block_size);
2075  read_block(n, q);
2076  p = q;
2077  C_[0].set_n(n);
2078  }
2079  if (REVISION(p) > revision_number + writable) {
2080  set_overwritten();
2081  RETURN(false);
2082  }
2083  if (GET_LEVEL(p) == 0) break;
2084  }
2085  c = DIR_END(p);
2086  AssertRel(DIR_START,<,c);
2087  }
2088  c -= D2;
2089  C_[0].c = c;
2090  RETURN(true);
2091 }
2092 
2093 bool
2095 {
2096  LOGCALL(DB, bool, "GlassTable::next_for_sequential", Literal("C_") | Literal("/*dummy*/"));
2097  const uint8_t * p = C_[0].get_p();
2098  Assert(p);
2099  int c = C_[0].c;
2100  AssertRel(c,<,DIR_END(p));
2101  c += D2;
2102  Assert(unsigned(c) < block_size);
2103  if (c == DIR_END(p)) {
2104  uint4 n = C_[0].get_n();
2105  while (true) {
2106  n++;
2107  if (n >= free_list.get_first_unused_block()) RETURN(false);
2108  if (writable) {
2109  if (n == C[0].get_n()) {
2110  // Block is a leaf block in the built-in cursor
2111  // (potentially in modified form).
2112  p = C_[0].clone(C[0]);
2113  } else {
2114  // Blocks in the built-in cursor may not have been written
2115  // to disk yet, so we have to check that the block number
2116  // isn't in the built-in cursor or we'll read an
2117  // uninitialised block (for which GET_LEVEL(p) will
2118  // probably return 0).
2119  int j;
2120  for (j = 1; j <= level; ++j) {
2121  if (n == C[j].get_n()) break;
2122  }
2123  if (j <= level) continue;
2124 
2125  // Block isn't in the built-in cursor, so the form on disk
2126  // is valid, so read it to check if it's the next level 0
2127  // block.
2128  uint8_t * q = C_[0].init(block_size);
2129  read_block(n, q);
2130  p = q;
2131  }
2132  } else {
2133  uint8_t * q = C_[0].init(block_size);
2134  read_block(n, q);
2135  p = q;
2136  }
2137  if (REVISION(p) > revision_number + writable) {
2138  set_overwritten();
2139  RETURN(false);
2140  }
2141  if (GET_LEVEL(p) == 0) break;
2142  }
2143  c = DIR_START;
2144  C_[0].set_n(n);
2145  }
2146  C_[0].c = c;
2147  RETURN(true);
2148 }
2149 
2150 bool
2152 {
2153  LOGCALL(DB, bool, "GlassTable::prev_default", Literal("C_") | j);
2154  const uint8_t * p = C_[j].get_p();
2155  int c = C_[j].c;
2156  AssertRel(DIR_START,<=,c);
2157  AssertRel(c,<,DIR_END(p));
2158  AssertRel(unsigned(DIR_END(p)),<=,block_size);
2159  if (c == DIR_START) {
2160  if (j == level) RETURN(false);
2161  if (!prev_default(C_, j + 1)) RETURN(false);
2162  p = C_[j].get_p();
2163  c = DIR_END(p);
2164  AssertRel(DIR_START,<,c);
2165  }
2166  c -= D2;
2167  C_[j].c = c;
2168  if (j > 0) {
2169  block_to_cursor(C_, j - 1, BItem(p, c).block_given_by());
2170  }
2171  RETURN(true);
2172 }
2173 
2174 bool
2176 {
2177  LOGCALL(DB, bool, "GlassTable::next_default", Literal("C_") | j);
2178  const uint8_t * p = C_[j].get_p();
2179  int c = C_[j].c;
2180  AssertRel(c,<,DIR_END(p));
2181  AssertRel(unsigned(DIR_END(p)),<=,block_size);
2182  c += D2;
2183  if (j > 0) {
2184  AssertRel(DIR_START,<,c);
2185  } else {
2186  AssertRel(DIR_START,<=,c);
2187  }
2188  // Sometimes c can be DIR_END(p) + 2 here it appears...
2189  if (c >= DIR_END(p)) {
2190  if (j == level) RETURN(false);
2191  if (!next_default(C_, j + 1)) RETURN(false);
2192  p = C_[j].get_p();
2193  c = DIR_START;
2194  }
2195  C_[j].c = c;
2196  if (j > 0) {
2197  block_to_cursor(C_, j - 1, BItem(p, c).block_given_by());
2198 #ifdef BTREE_DEBUG_FULL
2199  printf("Block in GlassTable:next_default");
2200  report_block_full(j - 1, C_[j - 1].get_n(), C_[j - 1].get_p());
2201 #endif /* BTREE_DEBUG_FULL */
2202  }
2203  RETURN(true);
2204 }
2205 
2206 void
2208 {
2209  throw Xapian::DatabaseClosedError("Database has been closed");
2210 }
char name[9]
Definition: dbcheck.cc:55
#define BLK_UNUSED
Definition: chert_cursor.h:31
const int DIR_START
Definition: chert_table.h:155
int GET_LEVEL(const uint8_t *b)
Definition: chert_table.h:151
unsigned REVISION(const uint8_t *b)
Definition: chert_table.h:150
const int D2
Definition: chert_table.h:122
int DIR_END(const uint8_t *b)
Definition: chert_table.h:154
A cursor pointing to a position in a Btree table, for reading several entries in order,...
Definition: glass_cursor.h:147
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:429
bool readahead_key(const string &key) const
void block_to_cursor(Glass::Cursor *C_, int j, uint4 n) const
Definition: glass_table.cc:317
void add(const std::string &key, const std::string &tag, bool already_compressed=false)
Add a key/tag pair to the table, replacing any existing pair with the same key.
bool Btree_modified
Set to true the first time the B-tree is modified.
Definition: glass_table.h:833
uint4 changed_n
the last block to be changed by an addition
Definition: glass_table.h:823
void alter()
Btree::alter(); is called when the B-tree is to be altered.
Definition: glass_table.cc:381
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:570
GlassFreeList free_list
List of free blocks.
Definition: glass_table.h:809
int seq_count
count of the number of successive instances of purely sequential addition, starting at SEQ_START_POIN...
Definition: glass_table.h:820
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:806
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:768
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:780
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:827
bool next_default(Glass::Cursor *C_, int j) const
uint4 root
the root block of the B-tree
Definition: glass_table.h:800
bool del(const std::string &key)
Delete an entry from the table.
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:610
uint8_t * split_p
Buffer used when splitting a block.
Definition: glass_table.h:895
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:797
bool exists() const
Determine whether the btree exists on disk.
void set_overwritten() const
Definition: glass_table.cc:296
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:713
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:839
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:657
bool find(Glass::Cursor *) const
find(C_) searches for the key of B->kt in the B-tree.
Definition: glass_table.cc:537
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_.
bool key_exists(const std::string &key) const
Check if a key exists in the Btree.
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:744
void add_leaf_item(Glass::LeafItem kt)
GlassTable::add_leaf_item(kt_) adds item kt_ to the leaf block.
Definition: glass_table.cc:857
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:771
GlassCursor * cursor_get() const
Get a cursor for reading from the table.
bool cursor_created_since_last_modification
Flag for tracking when cursors need to rebuild.
Definition: glass_table.h:842
void set_full_compaction(bool parity)
unsigned long cursor_version
Version count for tracking when cursors need to rebuild.
Definition: glass_table.h:845
void form_key(const std::string &key) const
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:821
std::string name
The path name of the B tree.
Definition: glass_table.h:815
bool lazy
If true, don't create the table until it's needed.
Definition: glass_table.h:903
bool single_file() const
Definition: glass_table.h:853
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:952
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:516
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:888
int handle
File descriptor of the table.
Definition: glass_table.h:794
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:803
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:781
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:898
bool get_exact_entry(const std::string &key, std::string &tag) const
Read an entry from the table, if and only if it is exactly that being asked for.
bool sequential
true iff the data has been written in a single write in sequential order.
Definition: glass_table.h:785
int flags
Flags like DB_NO_SYNC and DB_DANGEROUS.
Definition: glass_table.h:774
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:425
glass_revision_number_t revision_number
revision number of the opened B-tree.
Definition: glass_table.h:765
Key key() const
Definition: glass_table.h:326
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:330
T get_address() const
Definition: glass_table.h:321
int size() const
SIZE in diagram above.
Definition: glass_table.h:323
void set_truncated_key_and_block(Key newkey, int new_comp, int truncate_size, uint4 n)
Definition: glass_table.h:367
void set_key_and_block(Key newkey, uint4 n)
Definition: glass_table.h:359
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:389
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:384
static void setD(uint8_t *q, int c, int x)
Definition: glass_table.h:395
uint8_t * init(unsigned block_size)
Definition: glass_cursor.h:54
bool rewrite
true if the block is not the same as on disk, and so needs rewriting
Definition: glass_cursor.h:137
void destroy()
Definition: glass_cursor.h:83
int c
offset in the block's directory
Definition: glass_cursor.h:134
uint4 get_n() const
Get the block number.
Definition: glass_cursor.h:101
const uint8_t * clone(const Cursor &o)
Definition: glass_cursor.h:68
const uint8_t * get_p() const
Get pointer to block.
Definition: glass_cursor.h:116
void set_n(uint4 n)
Definition: glass_cursor.h:106
int length() const
Definition: glass_table.h:149
int size() const
SIZE in diagram above.
Definition: glass_table.h:182
bool last_component() const
Definition: glass_table.h:187
void append_chunk(std::string *tag) const
Definition: glass_table.h:193
int component_of() const
Definition: glass_table.h:188
bool get_compressed() const
Definition: glass_table.h:185
void init(T p_, int c)
Definition: glass_table.h:177
bool decompress_chunk(CompressionStream &comp_stream, string &tag) const
Definition: glass_table.h:202
static void setD(uint8_t *q, int c, int x)
Definition: glass_table.h:282
void set_num_entries(glass_tablesize_t n)
Definition: glass_version.h:72
const std::string & get_free_list() const
Definition: glass_version.h:69
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:76
bool get_sequential() const
Definition: glass_version.h:63
void set_level(int level_)
Definition: glass_version.h:71
unsigned get_blocksize() const
Definition: glass_version.h:64
void set_sequential(bool f)
Definition: glass_version.h:74
void set_free_list(const std::string &s)
Definition: glass_version.h:80
int get_level() const
Definition: glass_version.h:60
uint4 get_compress_min() const
Definition: glass_version.h:68
void set_root_is_fake(bool f)
Definition: glass_version.h:73
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:75
Indicates an attempt to access a closed database.
Definition: error.h:1097
DatabaseCorruptError indicates database corruption was detected.
Definition: error.h:409
DatabaseError indicates some sort of database related error.
Definition: error.h:367
DatabaseModifiedError indicates a database was modified.
Definition: error.h:539
DatabaseOpeningError indicates failure to open a database.
Definition: error.h:581
InvalidOperationError indicates the API was used in an invalid way.
Definition: error.h:283
UnimplementedError indicates an attempt to use an unimplemented feature.
Definition: error.h:325
#define rare(COND)
Definition: config.h:578
Constants in the Xapian namespace.
Debug logging macros.
#define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS)
Definition: debuglog.h:487
#define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS)
Definition: debuglog.h:489
#define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS)
Definition: debuglog.h:491
#define LOGCALL_VOID(CATEGORY, FUNC, PARAMS)
Definition: debuglog.h:488
#define LOGCALL_DTOR(CATEGORY, CLASS)
Definition: debuglog.h:490
#define RETURN(A)
Definition: debuglog.h:493
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:39
Glass changesets.
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
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:476
Btree implementation.
#define GLASS_BTREE_MAX_KEY_LEN
The largest possible value of a key_len.
Definition: glass_table.h:57
GlassVersion class.
#define true
Definition: header.h:8
#define false
Definition: header.h:9
uint32_t uint4
Definition: internaltypes.h:32
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:192
int io_open_block_wr(const char *filename, bool anew)
Open a block-based file for writing.
Definition: io_utils.cc:125
bool io_unlink(const std::string &filename)
Delete a file.
Definition: io_utils.cc:52
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:240
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:133
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:114
const int LEVEL_FREELIST
Freelist blocks have their level set to LEVEL_FREELIST.
Definition: glass_table.h:136
void SET_DIR_END(uint8_t *b, int x)
Definition: glass_table.h:121
int compare(ITEM1 a, ITEM2 b)
Compare two items by their keys.
Definition: glass_table.h:934
int TOTAL_FREE(const uint8_t *b)
Definition: glass_table.h:113
const int DIR_START
Definition: glass_table.h:115
void SET_LEVEL(uint8_t *b, int x)
Definition: glass_table.h:118
int GET_LEVEL(const uint8_t *b)
Definition: glass_table.h:111
@ 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:117
const int I2
Definition: glass_table.h:73
int MAX_FREE(const uint8_t *b)
Definition: glass_table.h:112
const int D2
Definition: glass_table.h:74
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:50
const int BYTES_PER_BLOCK_NUMBER
Definition: glass_table.h:60
uint4 REVISION(const uint8_t *b)
Definition: glass_table.h:110
const int X2
Definition: glass_table.h:75
void SET_TOTAL_FREE(uint8_t *b, int x)
Definition: glass_table.h:120
void SET_MAX_FREE(uint8_t *b, int x)
Definition: glass_table.h:119
const int K1
Definition: glass_table.h:72
string str(int value)
Convert int to std::string.
Definition: str.cc:90
int revision()
Report the revision of the library which the program is linked with.
Definition: xapian.h:142
XAPIAN_REVISION_TYPE rev
Revision number of a database.
Definition: types.h:133
const int DB_DANGEROUS
Update the database in-place.
Definition: constants.h:103
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:382
Provides wrappers with POSIXy semantics.
#define C(X)
Convert types to std::string.
Various handy helpers which std::string really should provide.
#define STRINGIZE(X)
The STRINGIZE macro converts its parameter into a string constant.
Definition: stringutils.h:36
Definition: pretty.h:45
Definition: header.h:151
functions for reading and writing different width words