1 /* glass_cursor.cc: Btree cursor implementation
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2012,2013,2015,2016 Olly Betts
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.
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.
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
24 #include "glass_cursor.h"
26 #include "safeerrno.h"
28 #include <xapian/error.h>
30 #include "glass_table.h"
34 using namespace Glass
;
36 #ifdef XAPIAN_DEBUG_LOG
38 hex_display_encode(const string
& input
)
40 const char * table
= "0123456789abcdef";
42 for (string::const_iterator i
= input
.begin(); i
!= input
.end(); ++i
) {
43 unsigned char val
= *i
;
45 result
+= table
[val
/ 16];
46 result
+= table
[val
% 16];
55 GlassCursor::GlassCursor(const GlassTable
*B_
, const Glass::Cursor
* C_
)
56 : is_positioned(false),
60 version(B_
->cursor_version
),
63 B
->cursor_created_since_last_modification
= true;
64 C
= new Glass::Cursor
[level
+ 1];
66 for (int j
= 0; j
<= level
; j
++) {
72 GlassCursor::rebuild()
74 int new_level
= B
->level
;
75 if (new_level
<= level
) {
76 for (int j
= new_level
; j
<= level
; ++j
) {
81 C
= new Cursor
[new_level
+ 1];
82 for (int i
= 0; i
< level
; i
++) {
86 for (int j
= level
; j
< new_level
; j
++) {
87 C
[j
].init(B
->block_size
);
91 C
[level
].clone(B
->C
[level
]);
92 version
= B
->cursor_version
;
93 B
->cursor_created_since_last_modification
= true;
96 GlassCursor::~GlassCursor()
98 // We must not use B here, as it may have already been destroyed.
105 LOGCALL(DB
, bool, "GlassCursor::next", NO_ARGS
);
106 Assert(!is_after_end
);
107 if (B
->cursor_version
!= version
) {
108 // find_entry() will call rebuild().
109 (void)find_entry(current_key
);
110 // If the key was found, we're now pointing to it, otherwise we're
111 // pointing to the entry before. Either way, we now want to move to
114 if (tag_status
== UNREAD
|| tag_status
== UNREAD_ON_LAST_CHUNK
) {
116 if (! B
->next(C
, 0)) {
117 is_positioned
= false;
120 if (tag_status
== UNREAD_ON_LAST_CHUNK
||
121 LeafItem(C
[0].get_p(), C
[0].c
).first_component()) {
122 is_positioned
= true;
128 if (!is_positioned
) {
133 get_key(¤t_key
);
136 LOGLINE(DB
, "Moved to entry: key=" << hex_display_encode(current_key
));
141 GlassCursor::find_entry(const string
&key
)
143 LOGCALL(DB
, bool, "GlassCursor::find_entry", key
);
144 if (B
->cursor_version
!= version
) {
148 is_after_end
= false;
152 is_positioned
= true;
153 if (key
.size() > GLASS_BTREE_MAX_KEY_LEN
) {
154 // Can't find key - too long to possibly be present, so find the
155 // truncated form but ignore "found".
156 B
->form_key(key
.substr(0, GLASS_BTREE_MAX_KEY_LEN
));
168 // Be lazy about stepping back to the first chunk, as we may never be
169 // asked to read the tag.
170 tag_status
= UNREAD_ON_LAST_CHUNK
;
171 if (C
[0].c
< DIR_START
) {
172 // It would be nice to be lazy about this too, but we need to
173 // be on an actual entry in order to read the key.
175 if (! B
->prev(C
, 0)) {
179 get_key(¤t_key
);
182 LOGLINE(DB
, "Found entry: key=" << hex_display_encode(current_key
));
187 GlassCursor::find_entry_lt(const string
&key
)
189 LOGCALL_VOID(DB
, "GlassCursor::find_entry_lt", key
);
190 if (!find_entry(key
)) {
191 // The entry wasn't found, so find_entry() left us on the entry before
192 // the one we asked for and we're done.
196 Assert(!is_after_end
);
197 Assert(is_positioned
);
199 if (! B
->prev(C
, 0)) {
200 is_positioned
= false;
203 tag_status
= UNREAD_ON_LAST_CHUNK
;
204 get_key(¤t_key
);
206 LOGLINE(DB
, "Found entry: key=" << hex_display_encode(current_key
));
210 GlassCursor::find_exact(const string
&key
)
212 LOGCALL(DB
, bool, "GlassCursor::find_exact", key
);
213 is_after_end
= false;
214 is_positioned
= false;
215 if (rare(key
.size() > GLASS_BTREE_MAX_KEY_LEN
)) {
216 // There can't be a match
220 if (B
->cursor_version
!= version
) {
229 B
->read_tag(C
, ¤t_tag
, false);
235 GlassCursor::find_entry_ge(const string
&key
)
237 LOGCALL(DB
, bool, "GlassCursor::find_entry_ge", key
);
238 if (B
->cursor_version
!= version
) {
242 is_after_end
= false;
246 is_positioned
= true;
247 if (key
.size() > GLASS_BTREE_MAX_KEY_LEN
) {
248 // Can't find key - too long to possibly be present, so find the
249 // truncated form but ignore "found".
250 B
->form_key(key
.substr(0, GLASS_BTREE_MAX_KEY_LEN
));
261 if (! B
->next(C
, 0)) {
263 is_positioned
= false;
266 Assert(LeafItem(C
[0].get_p(), C
[0].c
).first_component());
267 get_key(¤t_key
);
271 LOGLINE(DB
, "Found entry: key=" << hex_display_encode(current_key
));
276 GlassCursor::get_key(string
* key
) const
278 Assert(B
->level
<= level
);
279 Assert(is_positioned
);
281 (void)LeafItem(C
[0].get_p(), C
[0].c
).key().read(key
);
285 GlassCursor::read_tag(bool keep_compressed
)
287 LOGCALL(DB
, bool, "GlassCursor::read_tag", keep_compressed
);
288 if (tag_status
== UNREAD_ON_LAST_CHUNK
) {
289 // Back up to first chunk of this tag.
290 while (!LeafItem(C
[0].get_p(), C
[0].c
).first_component()) {
291 if (! B
->prev(C
, 0)) {
292 is_positioned
= false;
293 throw Xapian::DatabaseCorruptError("find_entry failed to find any entry at all!");
298 if (tag_status
== UNREAD
) {
299 Assert(B
->level
<= level
);
300 Assert(is_positioned
);
302 if (B
->read_tag(C
, ¤t_tag
, keep_compressed
)) {
303 tag_status
= COMPRESSED
;
305 tag_status
= UNCOMPRESSED
;
308 // We need to call B->next(...) after B->read_tag(...) so that the
309 // cursor ends up on the next key.
310 is_positioned
= B
->next(C
, 0);
312 LOGLINE(DB
, "tag=" << hex_display_encode(current_tag
));
314 RETURN(tag_status
== COMPRESSED
);
318 MutableGlassCursor::del()
320 Assert(!is_after_end
);
322 // MutableGlassCursor is only constructable from a non-const GlassTable*
323 // but we store that in the const GlassTable* "B" member of the GlassCursor
324 // class to avoid duplicating storage. So we know it is safe to cast away
325 // that const again here.
326 (const_cast<GlassTable
*>(B
))->del(current_key
);
328 // If we're iterating an older revision of the tree, then the deletion
329 // happens in a new (uncommitted) revision and the cursor still sees
330 // the deleted key. But if we're iterating the new uncommitted revision
331 // then the deleted key is no longer visible. We need to handle both
332 // cases - either find_entry_ge() finds the deleted key or not.
333 if (!find_entry_ge(current_key
)) return is_positioned
;