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