xapian-core  1.4.27
chert_cursor.cc
Go to the documentation of this file.
1 /* chert_cursor.cc: Btree cursor implementation
2  *
3  * Copyright 1999,2000,2001 BrightStation PLC
4  * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2012,2015 Olly Betts
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19  * USA
20  */
21 
22 #include <config.h>
23 
24 #include "chert_cursor.h"
25 
26 #include <cerrno>
27 
28 #include <xapian/error.h>
29 
30 #include "chert_table.h"
31 #include "debuglog.h"
32 #include "omassert.h"
33 
34 #ifdef XAPIAN_DEBUG_LOG
35 static string
36 hex_display_encode(const string & input)
37 {
38  const char * table = "0123456789abcdef";
39  string result;
40  for (string::const_iterator i = input.begin(); i != input.end(); ++i) {
41  unsigned char val = *i;
42  result += "\\x";
43  result += table[val / 16];
44  result += table[val % 16];
45  }
46 
47  return result;
48 }
49 #endif
50 
51 #define DIR_START 11
52 
54  : is_positioned(false),
55  is_after_end(false),
56  tag_status(UNREAD),
57  B(B_),
58  version(B_->cursor_version),
59  level(B_->level)
60 {
61  B->cursor_created_since_last_modification = true;
62  C = new Cursor[level + 1];
63 
64  for (int j = 0; j < level; j++) {
65  C[j].n = BLK_UNUSED;
66  C[j].p = new uint8_t[B->block_size];
67  }
68  C[level].n = B->C[level].n;
69  C[level].p = B->C[level].p;
70 }
71 
72 void
74 {
75  int new_level = B->level;
76  if (new_level <= level) {
77  for (int i = 0; i < new_level; i++) {
78  C[i].n = BLK_UNUSED;
79  }
80  for (int j = new_level; j < level; ++j) {
81  delete [] C[j].p;
82  }
83  } else {
84  Cursor * old_C = C;
85  C = new Cursor[new_level + 1];
86  for (int i = 0; i < level; i++) {
87  C[i].p = old_C[i].p;
88  C[i].n = BLK_UNUSED;
89  }
90  delete [] old_C;
91  for (int j = level; j < new_level; j++) {
92  C[j].p = new uint8_t[B->block_size];
93  C[j].n = BLK_UNUSED;
94  }
95  }
96  level = new_level;
97  C[level].n = B->C[level].n;
98  C[level].p = B->C[level].p;
99  version = B->cursor_version;
100  B->cursor_created_since_last_modification = true;
101 }
102 
104 {
105  // Use the value of level stored in the cursor rather than the
106  // Btree, since the Btree might have been deleted already.
107  for (int j = 0; j < level; j++) {
108  delete [] C[j].p;
109  }
110  delete [] C;
111 }
112 
113 bool
115 {
116  LOGCALL(DB, bool, "ChertCursor::prev", NO_ARGS);
118  if (B->cursor_version != version || !is_positioned) {
119  // Either the cursor needs rebuilding (in which case find_entry() will
120  // call rebuild() and then reposition the cursor), or we've read the
121  // last key and tag, and we're now not positioned (in which case we
122  // seek to the current key, and then it's as if we read the key but not
123  // the tag).
124  if (!find_entry(current_key)) {
125  // Exact entry was no longer there after rebuild(), and we've
126  // automatically ended up on the entry before it.
127  RETURN(true);
128  }
129  } else if (tag_status != UNREAD) {
130  while (true) {
131  if (! B->prev(C, 0)) {
132  is_positioned = false;
133  RETURN(false);
134  }
135  if (Item(C[0].p, C[0].c).component_of() == 1) {
136  break;
137  }
138  }
139  }
140 
141  while (true) {
142  if (! B->prev(C, 0)) {
143  is_positioned = false;
144  RETURN(false);
145  }
146  if (Item(C[0].p, C[0].c).component_of() == 1) {
147  break;
148  }
149  }
151  tag_status = UNREAD;
152 
153  LOGLINE(DB, "Moved to entry: key=" << hex_display_encode(current_key));
154  RETURN(true);
155 }
156 
157 bool
159 {
160  LOGCALL(DB, bool, "ChertCursor::next", NO_ARGS);
162  if (B->cursor_version != version) {
163  // find_entry() will call rebuild().
164  (void)find_entry(current_key);
165  // If the key was found, we're now pointing to it, otherwise we're
166  // pointing to the entry before. Either way, we now want to move to
167  // the next key.
168  }
169  if (tag_status == UNREAD) {
170  while (true) {
171  if (! B->next(C, 0)) {
172  is_positioned = false;
173  break;
174  }
175  if (Item(C[0].p, C[0].c).component_of() == 1) {
176  is_positioned = true;
177  break;
178  }
179  }
180  }
181 
182  if (!is_positioned) {
183  is_after_end = true;
184  RETURN(false);
185  }
186 
188  tag_status = UNREAD;
189 
190  LOGLINE(DB, "Moved to entry: key=" << hex_display_encode(current_key));
191  RETURN(true);
192 }
193 
194 bool
195 ChertCursor::find_entry(const string &key)
196 {
197  LOGCALL(DB, bool, "ChertCursor::find_entry", key);
198  if (B->cursor_version != version) {
199  rebuild();
200  }
201 
202  is_after_end = false;
203 
204  bool found;
205 
206  is_positioned = true;
207  if (key.size() > CHERT_BTREE_MAX_KEY_LEN) {
208  // Can't find key - too long to possibly be present, so find the
209  // truncated form but ignore "found".
210  B->form_key(key.substr(0, CHERT_BTREE_MAX_KEY_LEN));
211  (void)(B->find(C));
212  found = false;
213  } else {
214  B->form_key(key);
215  found = B->find(C);
216  }
217 
218  if (!found) {
219  if (C[0].c < DIR_START) {
220  C[0].c = DIR_START;
221  if (! B->prev(C, 0)) goto done;
222  }
223  while (Item(C[0].p, C[0].c).component_of() != 1) {
224  if (! B->prev(C, 0)) {
225  is_positioned = false;
226  throw Xapian::DatabaseCorruptError("find_entry failed to find any entry at all!");
227  }
228  }
229  }
230 done:
231 
232  if (found)
233  current_key = key;
234  else
236  tag_status = UNREAD;
237 
238  LOGLINE(DB, "Found entry: key=" << hex_display_encode(current_key));
239  RETURN(found);
240 }
241 
242 bool
243 ChertCursor::find_entry_ge(const string &key)
244 {
245  LOGCALL(DB, bool, "ChertCursor::find_entry_ge", key);
246  if (B->cursor_version != version) {
247  rebuild();
248  }
249 
250  is_after_end = false;
251 
252  bool found;
253 
254  is_positioned = true;
255  if (key.size() > CHERT_BTREE_MAX_KEY_LEN) {
256  // Can't find key - too long to possibly be present, so find the
257  // truncated form but ignore "found".
258  B->form_key(key.substr(0, CHERT_BTREE_MAX_KEY_LEN));
259  (void)(B->find(C));
260  found = false;
261  } else {
262  B->form_key(key);
263  found = B->find(C);
264  }
265 
266  if (found) {
267  current_key = key;
268  } else {
269  if (! B->next(C, 0)) {
270  is_after_end = true;
271  is_positioned = false;
272  RETURN(false);
273  }
274  Assert(Item(C[0].p, C[0].c).component_of() == 1);
276  }
277  tag_status = UNREAD;
278 
279  LOGLINE(DB, "Found entry: key=" << hex_display_encode(current_key));
280  RETURN(found);
281 }
282 
283 void
284 ChertCursor::get_key(string * key) const
285 {
286  Assert(B->level <= level);
288 
289  (void)Item(C[0].p, C[0].c).key().read(key);
290 }
291 
292 bool
293 ChertCursor::read_tag(bool keep_compressed)
294 {
295  LOGCALL(DB, bool, "ChertCursor::read_tag", keep_compressed);
296  if (tag_status == UNREAD) {
297  Assert(B->level <= level);
299 
300  if (B->read_tag(C, &current_tag, keep_compressed)) {
302  } else {
304  }
305 
306  // We need to call B->next(...) after B->read_tag(...) so that the
307  // cursor ends up on the next key.
308  is_positioned = B->next(C, 0);
309 
310  LOGLINE(DB, "tag=" << hex_display_encode(current_tag));
311  }
313 }
314 
315 bool
317 {
319 
320  // MutableChertCursor is only constructable from a non-const ChertTable*
321  // but we store that in the const ChertTable* "B" member of the ChertCursor
322  // class to avoid duplicating storage. So we know it is safe to cast away
323  // that const again here.
324  (const_cast<ChertTable*>(B))->del(current_key);
325 
326  // If we're iterating an older revision of the tree, then the deletion
327  // happens in a new (uncommitted) revision and the cursor still sees
328  // the deleted key. But if we're iterating the new uncommitted revision
329  // then the deleted key is no longer visible. We need to handle both
330  // cases - either find_entry_ge() finds the deleted key or not.
332  return next();
333 }
bool is_positioned
Whether the cursor is positioned at a valid entry.
Definition: chert_cursor.h:86
#define RETURN(A)
Definition: debuglog.h:493
#define Assert(COND)
Definition: omassert.h:122
bool prev()
Move to the previous key.
Definition: unittest.cc:678
int c
offset in the block&#39;s directory
Definition: chert_cursor.h:47
Key key() const
Definition: chert_table.h:214
const ChertTable * B
The Btree table.
Definition: chert_cursor.h:98
bool next()
Advance to the next key.
int level
The value of level in the Btree structure.
Definition: chert_cursor.h:107
Cursor * C
Pointer to an array of Cursors.
Definition: chert_cursor.h:102
Definition: header.h:63
Class managing a Btree table in a Chert database.
Definition: chert_table.h:347
#define BLK_UNUSED
Definition: chert_cursor.h:31
int component_of() const
Definition: chert_table.h:208
#define false
Definition: header.h:9
Hierarchy of classes which Xapian can throw as exceptions.
bool find_entry_ge(const string &key)
Position the cursor on the lowest entry with key >= key.
string current_tag
Current tag pointed to by cursor.
Definition: chert_cursor.h:154
bool is_after_end
Whether the cursor is off the end of the table.
Definition: chert_cursor.h:90
void rebuild()
Rebuild the cursor.
Definition: chert_cursor.cc:73
enum ChertCursor::@1 tag_status
Status of the current_tag member.
Interface to Btree cursors.
~ChertCursor()
Destroy the ChertCursor.
uint8_t * p
pointer to a block
Definition: chert_cursor.h:45
bool del()
Delete the current key/tag pair, leaving the cursor on the next entry.
#define DIR_START
Definition: chert_cursor.cc:51
void get_key(string *key) const
Get the key.
DatabaseCorruptError indicates database corruption was detected.
Definition: error.h:409
string current_key
Current key pointed to by cursor.
Definition: chert_cursor.h:149
Btree implementation.
uint4 n
block number
Definition: chert_cursor.h:56
#define CHERT_BTREE_MAX_KEY_LEN
The largest possible value of a key_len.
Definition: chert_table.h:101
bool read_tag(bool keep_compressed=false)
Read the tag from the table and store it in current_tag.
unsigned long version
Definition: chert_cursor.h:104
ChertCursor(const ChertCursor &)
Copying not allowed.
Various assertion macros.
#define LOGLINE(a, b)
Definition: debuglog.h:494
bool find_entry(const string &key)
Position the cursor on the highest entry with key <= key.
Debug logging macros.
#define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS)
Definition: debuglog.h:487
void read(std::string *key) const
Definition: chert_table.h:172