xapian-core  2.0.0
glass_cursor.cc
Go to the documentation of this file.
1 
4 /* Copyright 1999,2000,2001 BrightStation PLC
5  * Copyright 2002-2024 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, see
19  * <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 
24 #include "glass_cursor.h"
25 
26 #include <xapian/error.h>
27 
28 #include "glass_table.h"
29 #include "debuglog.h"
30 #include "omassert.h"
31 
32 #include <string_view>
33 
34 using namespace Glass;
35 using namespace std;
36 
37 #ifdef XAPIAN_DEBUG_LOG
38 static string
39 hex_display_encode(string_view input)
40 {
41  const char* table = "0123456789abcdef";
42  string result;
43  for (unsigned char val : input) {
44  result += "\\x";
45  result += table[val >> 4];
46  result += table[val & 0x0f];
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);
234  is_positioned = true;
235 
236  RETURN(true);
237 }
238 
239 bool
241 {
242  LOGCALL(DB, bool, "GlassCursor::find_entry_ge", key);
243  if (B->cursor_version != version) {
244  rebuild();
245  }
246 
247  is_after_end = false;
248 
249  bool found;
250 
251  is_positioned = true;
252  if (key.size() > GLASS_BTREE_MAX_KEY_LEN) {
253  // Can't find key - too long to possibly be present, so find the
254  // truncated form but ignore "found".
255  B->form_key(key.substr(0, GLASS_BTREE_MAX_KEY_LEN));
256  (void)(B->find(C));
257  found = false;
258  } else {
259  B->form_key(key);
260  found = B->find(C);
261  }
262 
263  if (found) {
264  current_key = key;
265  } else {
266  if (!B->next(C, 0)) {
267  is_after_end = true;
268  is_positioned = false;
269  RETURN(false);
270  }
271  Assert(LeafItem(C[0].get_p(), C[0].c).first_component());
273  }
274  tag_status = UNREAD;
275 
276  LOGLINE(DB, "Found entry: key=" << hex_display_encode(current_key));
277  RETURN(found);
278 }
279 
280 void
281 GlassCursor::get_key(string * key) const
282 {
283  Assert(B->level <= level);
285 
286  (void)LeafItem(C[0].get_p(), C[0].c).key().read(key);
287 }
288 
289 bool
290 GlassCursor::read_tag(bool keep_compressed)
291 {
292  LOGCALL(DB, bool, "GlassCursor::read_tag", keep_compressed);
294  // Back up to first chunk of this tag.
295  while (!LeafItem(C[0].get_p(), C[0].c).first_component()) {
296  if (!B->prev(C, 0)) {
297  is_positioned = false;
298  throw Xapian::DatabaseCorruptError("find_entry failed to find any entry at all!");
299  }
300  }
301  tag_status = UNREAD;
302  }
303  if (tag_status == UNREAD) {
304  Assert(B->level <= level);
306 
307  if (B->read_tag(C, &current_tag, keep_compressed)) {
309  } else {
311  }
312 
313  // We need to call B->next(...) after B->read_tag(...) so that the
314  // cursor ends up on the next key.
315  is_positioned = B->next(C, 0);
316 
317  LOGLINE(DB, "tag=" << hex_display_encode(current_tag));
318  }
320 }
321 
322 bool
324 {
326 
327  // MutableGlassCursor is only constructable from a non-const GlassTable*
328  // but we store that in the const GlassTable* "B" member of the GlassCursor
329  // class to avoid duplicating storage. So we know it is safe to cast away
330  // that const again here.
331  (const_cast<GlassTable*>(B))->del(current_key);
332 
333  // If we're iterating an older revision of the tree, then the deletion
334  // happens in a new (uncommitted) revision and the cursor still sees
335  // the deleted key. But if we're iterating the new uncommitted revision
336  // then the deleted key is no longer visible. We need to handle both
337  // cases - either find_entry_ge() finds the deleted key or not.
339  return next();
340 }
341 
342 #ifdef DISABLE_GPL_LIBXAPIAN
343 # error GPL source we cannot relicense included in libxapian
344 #endif
Definition: unittest.cc:660
const GlassTable * B
The Btree table.
Definition: glass_cursor.h:179
void get_key(string *key) const
Get the key.
@ UNREAD_ON_LAST_CHUNK
Definition: glass_cursor.h:175
string current_key
Current key pointed to by cursor.
Definition: glass_cursor.h:239
bool find_exact(const string &key)
Position the cursor exactly on a key.
bool find_entry(const string &key)
Position the cursor on the highest entry with key <= key.
bool read_tag(bool keep_compressed=false)
Read the tag from the table and store it in current_tag.
int level
The value of level in the Btree structure.
Definition: glass_cursor.h:188
void rebuild()
Rebuild the cursor.
Definition: glass_cursor.cc:71
bool next()
Advance to the next key.
unsigned long version
Definition: glass_cursor.h:185
bool find_entry_ge(std::string_view key)
Position the cursor on the lowest entry with key >= key.
string current_tag
Current tag pointed to by cursor.
Definition: glass_cursor.h:244
~GlassCursor()
Destroy the GlassCursor.
Definition: glass_cursor.cc:99
void find_entry_lt(const string &key)
Position the cursor on the highest entry with key < key.
enum GlassCursor::@6 tag_status
Status of the current_tag member.
bool is_after_end
Whether the cursor is off the end of the table.
Definition: glass_cursor.h:171
bool is_positioned
Whether the cursor is positioned at a valid entry.
Definition: glass_cursor.h:167
Glass::Cursor * C
Pointer to an array of Cursors.
Definition: glass_cursor.h:183
GlassCursor(const GlassCursor &)
Copying not allowed.
Class managing a Btree table in a Glass database.
Definition: glass_table.h:432
uint8_t * init(unsigned block_size)
Definition: glass_cursor.h:55
void destroy()
Definition: glass_cursor.h:84
int c
offset in the block's directory
Definition: glass_cursor.h:135
void swap(Cursor &o)
Definition: glass_cursor.h:78
const uint8_t * clone(const Cursor &o)
Definition: glass_cursor.h:69
bool first_component() const
Definition: glass_table.h:188
bool del()
Delete the current key/tag pair, leaving the cursor on the next entry.
DatabaseCorruptError indicates database corruption was detected.
Definition: error.h:397
#define rare(COND)
Definition: config.h:607
Debug logging macros.
#define RETURN(...)
Definition: debuglog.h:484
#define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS)
Definition: debuglog.h:478
#define LOGLINE(a, b)
Definition: debuglog.h:485
#define LOGCALL_VOID(CATEGORY, FUNC, PARAMS)
Definition: debuglog.h:479
Hierarchy of classes which Xapian can throw as exceptions.
#define DIR_START
Definition: glass_cursor.cc:52
Interface to Btree cursors.
Btree implementation.
#define GLASS_BTREE_MAX_KEY_LEN
The largest possible value of a key_len.
Definition: glass_table.h:56
#define false
Definition: header.h:9
Various assertion macros.
#define Assert(COND)
Definition: omassert.h:122
Definition: header.h:87