Branch data Line data Source code
1 : : /** @file chert_values.cc
2 : : * @brief ChertValueManager class
3 : : */
4 : : /* Copyright (C) 2008,2009 Olly Betts
5 : : * Copyright (C) 2008,2009 Lemur Consulting Ltd
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License as published by
9 : : * the Free Software Foundation; either version 2 of the License, or
10 : : * (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 USA
20 : : */
21 : :
22 : : #include <config.h>
23 : :
24 : : #include "chert_values.h"
25 : :
26 : : #include "chert_cursor.h"
27 : : #include "chert_postlist.h"
28 : : #include "chert_termlist.h"
29 : : #include "debuglog.h"
30 : : #include "document.h"
31 : : #include "pack.h"
32 : :
33 : : #include "xapian/error.h"
34 : : #include "xapian/valueiterator.h"
35 : :
36 : : #include <algorithm>
37 : : #include "autoptr.h"
38 : :
39 : : using namespace std;
40 : :
41 : : // FIXME:
42 : : // * put the "used slots" entry in the same termlist tag as the terms?
43 : : // * multi-values?
44 : : // * values named instead of numbered?
45 : :
46 : : /** Generate a key for the "used slots" data. */
47 : : inline string
48 : 0 : make_slot_key(Xapian::docid did)
49 : : {
50 : : LOGCALL_STATIC(DB, string, "make_slot_key", did);
51 : : // Add an extra character so that it can't clash with a termlist entry key
52 : : // and will sort just after the corresponding termlist entry key.
53 : : // FIXME: should we store this in the *same entry* as the list of terms?
54 : 0 : string key;
55 : 0 : pack_uint_preserving_sort(key, did);
56 : 0 : key += '\0';
57 : 0 : RETURN(key);
58 : : }
59 : :
60 : : /** Generate a key for a value statistics item. */
61 : : inline string
62 : 0 : make_valuestats_key(Xapian::valueno slot)
63 : : {
64 : : LOGCALL_STATIC(DB, string, "make_valuestats_key", slot);
65 : 0 : string key("\0\xd0", 2);
66 : 0 : pack_uint_last(key, slot);
67 : 0 : RETURN(key);
68 : : }
69 : :
70 : : void
71 : 1682307 : ValueChunkReader::assign(const char * p_, size_t len, Xapian::docid did_)
72 : : {
73 : 1682307 : p = p_;
74 : 1682307 : end = p_ + len;
75 : 1682307 : did = did_;
76 [ - + ]: 1682307 : if (!unpack_string(&p, end, value))
77 : 0 : throw Xapian::DatabaseCorruptError("Failed to unpack first value");
78 : 1682307 : }
79 : :
80 : : void
81 : 45048 : ValueChunkReader::next()
82 : : {
83 [ + + ]: 45048 : if (p == end) {
84 : 1070 : p = NULL;
85 : 1070 : return;
86 : : }
87 : :
88 : : Xapian::docid delta;
89 [ - + ]: 43978 : if (!unpack_uint(&p, end, &delta))
90 : 0 : throw Xapian::DatabaseCorruptError("Failed to unpack streamed value docid");
91 : 43978 : did += delta + 1;
92 [ - + ]: 43978 : if (!unpack_string(&p, end, value))
93 : 45048 : throw Xapian::DatabaseCorruptError("Failed to unpack streamed value");
94 : : }
95 : :
96 : : void
97 : 1721258 : ValueChunkReader::skip_to(Xapian::docid target)
98 : : {
99 [ + - ][ + + ]: 1721258 : if (p == NULL || target <= did)
100 : 63908 : return;
101 : :
102 : : size_t value_len;
103 [ + + ]: 347523596 : while (p != end) {
104 : : // Get the next docid
105 : : Xapian::docid delta;
106 [ - + ]: 347510570 : if (rare(!unpack_uint(&p, end, &delta)))
107 : 0 : throw Xapian::DatabaseCorruptError("Failed to unpack streamed value docid");
108 : 347510570 : did += delta + 1;
109 : :
110 : : // Get the length of the string
111 [ - + ]: 347510570 : if (rare(!unpack_uint(&p, end, &value_len))) {
112 : 0 : throw Xapian::DatabaseCorruptError("Failed to unpack streamed value length");
113 : : }
114 : :
115 : : // Check that it's not too long
116 [ - + ]: 347510570 : if (rare(value_len > size_t(end - p))) {
117 : 0 : throw Xapian::DatabaseCorruptError("Failed to unpack streamed value");
118 : : }
119 : :
120 : : // Assign the value and return only if we've reached the target
121 [ + + ]: 347510570 : if (did >= target) {
122 : 1644324 : value.assign(p, value_len);
123 : 1644324 : p += value_len;
124 : 1644324 : return;
125 : : }
126 : 345866246 : p += value_len;
127 : : }
128 : 1721258 : p = NULL;
129 : : }
130 : :
131 : : void
132 : 287909 : ChertValueManager::add_value(Xapian::docid did, Xapian::valueno slot,
133 : : const string & val)
134 : : {
135 : 287909 : map<Xapian::valueno, map<Xapian::docid, string> >::iterator i;
136 : 287909 : i = changes.find(slot);
137 [ + + ]: 287909 : if (i == changes.end()) {
138 : 166051 : i = changes.insert(make_pair(slot, map<Xapian::docid, string>())).first;
139 : : }
140 : 287909 : i->second[did] = val;
141 : 287909 : }
142 : :
143 : : void
144 : 8153 : ChertValueManager::remove_value(Xapian::docid did, Xapian::valueno slot)
145 : : {
146 : 8153 : map<Xapian::valueno, map<Xapian::docid, string> >::iterator i;
147 : 8153 : i = changes.find(slot);
148 [ + + ]: 8153 : if (i == changes.end()) {
149 : 113 : i = changes.insert(make_pair(slot, map<Xapian::docid, string>())).first;
150 : : }
151 : 8153 : i->second[did] = string();
152 : 8153 : }
153 : :
154 : : Xapian::docid
155 : 1668073 : ChertValueManager::get_chunk_containing_did(Xapian::valueno slot,
156 : : Xapian::docid did,
157 : : string &chunk) const
158 : : {
159 : : LOGCALL(DB, Xapian::docid, "ChertValueManager::get_chunk_containing_did", slot | did | chunk);
160 : 1668073 : AutoPtr<ChertCursor> cursor(postlist_table->cursor_get());
161 [ - + ]: 1668071 : if (!cursor.get()) return 0;
162 : :
163 : 1668071 : bool exact = cursor->find_entry(make_valuechunk_key(slot, did));
164 [ + + ]: 1668071 : if (!exact) {
165 : : // If we didn't find a chunk starting with docid did, then we need
166 : : // to check if the chunk contains did.
167 : 1604499 : const char * p = cursor->current_key.data();
168 : 1604499 : const char * end = p + cursor->current_key.size();
169 : :
170 : : // Check that it is a value stream chunk.
171 [ + + + - ]: 1604499 : if (end - p < 2 || *p++ != '\0' || *p++ != '\xd8') return 0;
[ - + ][ + + ]
172 : :
173 : : // Check that it's for the right value slot.
174 : : Xapian::valueno v;
175 [ - + ]: 1603496 : if (!unpack_uint(&p, end, &v)) {
176 : 0 : throw Xapian::DatabaseCorruptError("Bad value key");
177 : : }
178 [ + + ]: 1603496 : if (v != slot) return 0;
179 : :
180 : : // And get the first docid for the chunk so we can return it.
181 [ + - ][ - + ]: 1603489 : if (!unpack_uint_preserving_sort(&p, end, &did) || p != end) {
[ - + ]
182 : 0 : throw Xapian::DatabaseCorruptError("Bad value key");
183 : : }
184 : : }
185 : :
186 : 1667061 : cursor->read_tag();
187 : 1667061 : swap(chunk, cursor->current_tag);
188 : :
189 : 1668071 : return did;
190 : : }
191 : :
192 : : static const size_t CHUNK_SIZE_THRESHOLD = 2000;
193 : :
194 : : static const Xapian::docid MAX_DOCID = static_cast<Xapian::docid>(-1);
195 : :
196 : : class ValueUpdater {
197 : : ChertPostListTable * table;
198 : :
199 : : Xapian::valueno slot;
200 : :
201 : : string ctag;
202 : :
203 : : ValueChunkReader reader;
204 : :
205 : : string tag;
206 : :
207 : : Xapian::docid prev_did;
208 : :
209 : : Xapian::docid first_did;
210 : :
211 : : Xapian::docid new_first_did;
212 : :
213 : : Xapian::docid last_allowed_did;
214 : :
215 : 314111 : void append_to_stream(Xapian::docid did, const string & value) {
216 : : Assert(did);
217 [ + + ]: 314111 : if (tag.empty()) {
218 : 167428 : new_first_did = did;
219 : : } else {
220 : : AssertRel(did,>,prev_did);
221 : 146683 : pack_uint(tag, did - prev_did - 1);
222 : : }
223 : 314111 : prev_did = did;
224 : 314111 : pack_string(tag, value);
225 [ + + ]: 314111 : if (tag.size() >= CHUNK_SIZE_THRESHOLD) write_tag();
226 : 314111 : }
227 : :
228 : 167518 : void write_tag() {
229 : : // If the first docid has changed, delete the old entry.
230 [ + + ][ + + ]: 167518 : if (first_did && new_first_did != first_did) {
231 : 128 : table->del(make_valuechunk_key(slot, first_did));
232 : : }
233 [ + + ]: 167518 : if (!tag.empty()) {
234 : 167428 : table->add(make_valuechunk_key(slot, new_first_did), tag);
235 : : }
236 : 167518 : first_did = 0;
237 : 167518 : tag.resize(0);
238 : 167518 : }
239 : :
240 : : public:
241 : 166163 : ValueUpdater(ChertPostListTable * table_, Xapian::valueno slot_)
242 : 166163 : : table(table_), slot(slot_), first_did(0), last_allowed_did(0) { }
243 : :
244 : 166163 : ~ValueUpdater() {
245 [ + + ]: 168264 : while (!reader.at_end()) {
246 : : // FIXME: use skip_to and some splicing magic instead?
247 : 2101 : append_to_stream(reader.get_docid(), reader.get_value());
248 : 2101 : reader.next();
249 : : }
250 : 166163 : write_tag();
251 : 166163 : }
252 : :
253 : 288442 : void update(Xapian::docid did, const string & value) {
254 [ + + ][ + + ]: 288442 : if (last_allowed_did && did > last_allowed_did) {
255 : : // The next change needs to go in a later existing chunk than the
256 : : // one we're currently updating, so we copy over the rest of the
257 : : // entries from the current chunk, write out the updated chunk and
258 : : // drop through to the case below will read in that later chunk.
259 : : // FIXME: use some string splicing magic instead of this loop.
260 [ + + ]: 3308 : while (!reader.at_end()) {
261 : : // last_allowed_did should be an upper bound for this chunk.
262 : : AssertRel(reader.get_docid(),<=,last_allowed_did);
263 : 3259 : append_to_stream(reader.get_docid(), reader.get_value());
264 : 3259 : reader.next();
265 : : }
266 : 49 : write_tag();
267 : 49 : last_allowed_did = 0;
268 : : }
269 [ + + ]: 288442 : if (last_allowed_did == 0) {
270 : 166212 : last_allowed_did = MAX_DOCID;
271 : : Assert(tag.empty());
272 : 166212 : new_first_did = 0;
273 : 166212 : AutoPtr<ChertCursor> cursor(table->cursor_get());
274 [ + + ]: 166212 : if (cursor->find_entry(make_valuechunk_key(slot, did))) {
275 : : // We found an exact match, so the first docid is the one
276 : : // we looked for.
277 : 148 : first_did = did;
278 : : } else {
279 : : Assert(!cursor->after_end());
280 : : // Otherwise we need to unpack it from the key we found.
281 : : // We may have found a non-value-chunk entry in which case
282 : : // docid_from_key() returns 0.
283 : 166064 : first_did = docid_from_key(slot, cursor->current_key);
284 : : }
285 : :
286 : : // If there are no further chunks, then the last docid that can go
287 : : // in this chunk is the highest valid docid. If there are further
288 : : // chunks then it's one less than the first docid of the next
289 : : // chunk.
290 [ + + ]: 166212 : if (first_did) {
291 : : // We found a value chunk.
292 : 172 : cursor->read_tag();
293 : : // FIXME:swap(cursor->current_tag, ctag);
294 : 172 : ctag = cursor->current_tag;
295 : 172 : reader.assign(ctag.data(), ctag.size(), first_did);
296 : : }
297 [ + + ]: 166212 : if (cursor->next()) {
298 : 166164 : const string & key = cursor->current_key;
299 : 166164 : Xapian::docid next_first_did = docid_from_key(slot, key);
300 [ + + ]: 166164 : if (next_first_did) last_allowed_did = next_first_did - 1;
301 : : Assert(last_allowed_did);
302 : : AssertRel(last_allowed_did,>=,first_did);
303 : 166212 : }
304 : : }
305 : :
306 : : // Copy over entries until we get to the one we want to
307 : : // add/modify/delete.
308 : : // FIXME: use skip_to and some splicing magic instead?
309 [ + + ][ + + ]: 312072 : while (!reader.at_end() && reader.get_docid() < did) {
[ + + ]
310 : 23630 : append_to_stream(reader.get_docid(), reader.get_value());
311 : 23630 : reader.next();
312 : : }
313 [ + + ][ + - ]: 288442 : if (!reader.at_end() && reader.get_docid() == did) reader.next();
[ + + ]
314 [ + + ]: 288442 : if (!value.empty()) {
315 : : // Add/update entry for did.
316 : 285121 : append_to_stream(did, value);
317 : : }
318 : 288442 : }
319 : : };
320 : :
321 : : void
322 : 542 : ChertValueManager::merge_changes()
323 : : {
324 [ + - ]: 542 : if (termlist_table->is_open()) {
325 : 542 : map<Xapian::docid, string>::const_iterator i;
326 [ + + ]: 45944 : for (i = slots.begin(); i != slots.end(); ++i) {
327 : 45402 : const string & enc = i->second;
328 : 45402 : string key = make_slot_key(i->first);
329 [ + + ]: 45402 : if (!enc.empty()) {
330 : 42366 : termlist_table->add(key, i->second);
331 : : } else {
332 : 3036 : termlist_table->del(key);
333 : : }
334 : : }
335 : 542 : slots.clear();
336 : : }
337 : :
338 : : {
339 : 542 : map<Xapian::valueno, map<Xapian::docid, string> >::const_iterator i;
340 [ + + ]: 166705 : for (i = changes.begin(); i != changes.end(); ++i) {
341 : 166163 : Xapian::valueno slot = i->first;
342 : 166163 : ValueUpdater updater(postlist_table, slot);
343 : 166163 : const map<Xapian::docid, string> & slot_changes = i->second;
344 : 166163 : map<Xapian::docid, string>::const_iterator j;
345 [ + + ]: 454605 : for (j = slot_changes.begin(); j != slot_changes.end(); ++j) {
346 : 288442 : updater.update(j->first, j->second);
347 : : }
348 : : }
349 : 542 : changes.clear();
350 : : }
351 : 542 : }
352 : :
353 : : void
354 : 77512 : ChertValueManager::add_document(Xapian::docid did, const Xapian::Document &doc,
355 : : map<Xapian::valueno, ValueStats> & value_stats)
356 : : {
357 : : // FIXME: Use BitWriter and interpolative coding? Or is it not worthwhile
358 : : // for this?
359 : 77512 : string slots_used;
360 : 77512 : Xapian::valueno prev_slot = static_cast<Xapian::valueno>(-1);
361 : 77512 : Xapian::ValueIterator it = doc.values_begin();
362 [ + + ]: 365421 : while (it != doc.values_end()) {
363 : 287909 : Xapian::valueno slot = it.get_valueno();
364 : 287909 : string value = *it;
365 : :
366 : : // Update the statistics.
367 : 287909 : std::pair<map<Xapian::valueno, ValueStats>::iterator, bool> i;
368 [ - + ]: 287909 : i = value_stats.insert(make_pair(slot, ValueStats()));
369 : 287909 : ValueStats & stats = i.first->second;
370 [ + + ]: 287909 : if (i.second) {
371 : : // There were no statistics stored already, so read them.
372 : 166051 : get_value_stats(slot, stats);
373 : : }
374 : :
375 : : // Now, modify the stored statistics.
376 [ + + ]: 287909 : if ((stats.freq)++ == 0) {
377 : : // If the value count was previously zero, set the upper and lower
378 : : // bounds to the newly added value.
379 : 166058 : stats.lower_bound = value;
380 : 166058 : stats.upper_bound = value;
381 : : } else {
382 : : // Otherwise, simply make sure they reflect the new value.
383 [ + + ]: 121851 : if (value < stats.lower_bound) {
384 : 535 : stats.lower_bound = value;
385 [ + + ]: 121316 : } else if (value > stats.upper_bound) {
386 : 847 : stats.upper_bound = value;
387 : : }
388 : : }
389 : :
390 : 287909 : add_value(did, slot, value);
391 [ + - ]: 287909 : if (termlist_table->is_open()) {
392 : 287909 : pack_uint(slots_used, slot - prev_slot - 1);
393 : 287909 : prev_slot = slot;
394 : : }
395 : 287909 : ++it;
396 : : }
397 [ + + ][ + + ]: 77512 : if (slots_used.empty() && slots.find(did) == slots.end()) {
[ + + ]
398 : : // Adding a new document with no values which we didn't just remove.
399 : : } else {
400 : 48741 : swap(slots[did], slots_used);
401 : 77512 : }
402 : 77512 : }
403 : :
404 : : void
405 : 19080 : ChertValueManager::delete_document(Xapian::docid did,
406 : : map<Xapian::valueno, ValueStats> & value_stats)
407 : : {
408 : : Assert(termlist_table->is_open());
409 : 19080 : map<Xapian::docid, string>::iterator it = slots.find(did);
410 : 19080 : string s;
411 [ + + ]: 19080 : if (it != slots.end()) {
412 : 3373 : swap(s, it->second);
413 : : } else {
414 : : // Get from table, making a swift exit if this document has no values.
415 [ + + ]: 15707 : if (!termlist_table->get_exact_entry(make_slot_key(did), s)) return;
416 : 5179 : slots.insert(make_pair(did, string()));
417 : : }
418 : 8552 : const char * p = s.data();
419 : 8552 : const char * end = p + s.size();
420 : 8552 : Xapian::valueno prev_slot = static_cast<Xapian::valueno>(-1);
421 [ + + ]: 16705 : while (p != end) {
422 : : Xapian::valueno slot;
423 [ - + ]: 8153 : if (!unpack_uint(&p, end, &slot)) {
424 : 0 : throw Xapian::DatabaseCorruptError("Value slot encoding corrupt");
425 : : }
426 : 8153 : slot += prev_slot + 1;
427 : 8153 : prev_slot = slot;
428 : :
429 : 8153 : std::pair<map<Xapian::valueno, ValueStats>::iterator, bool> i;
430 [ - + ]: 8153 : i = value_stats.insert(make_pair(slot, ValueStats()));
431 : 8153 : ValueStats & stats = i.first->second;
432 [ + + ]: 8153 : if (i.second) {
433 : : // There were no statistics stored already, so read them.
434 : 113 : get_value_stats(slot, stats);
435 : : }
436 : :
437 : : // Now, modify the stored statistics.
438 : : AssertRelParanoid(stats.freq, >, 0);
439 [ + + ]: 8153 : if (--(stats.freq) == 0) {
440 : 78 : stats.lower_bound.resize(0);
441 : 78 : stats.upper_bound.resize(0);
442 : : }
443 : :
444 : 8153 : remove_value(did, slot);
445 : 19080 : }
446 : : }
447 : :
448 : : void
449 : 9189 : ChertValueManager::replace_document(Xapian::docid did,
450 : : const Xapian::Document &doc,
451 : : map<Xapian::valueno, ValueStats> & value_stats)
452 : : {
453 : : // Load the values into the document from the database, if they haven't
454 : : // been already. (If we don't do this before deleting the old values,
455 : : // replacing a document with itself will lose the values.)
456 : 9189 : doc.internal->need_values();
457 : 9189 : delete_document(did, value_stats);
458 : 9189 : add_document(did, doc, value_stats);
459 : 9189 : }
460 : :
461 : : string
462 : 1675969 : ChertValueManager::get_value(Xapian::docid did, Xapian::valueno slot) const
463 : : {
464 : 1675969 : map<Xapian::valueno, map<Xapian::docid, string> >::const_iterator i;
465 : 1675969 : i = changes.find(slot);
466 [ + + ]: 1675969 : if (i != changes.end()) {
467 : 18551 : map<Xapian::docid, string>::const_iterator j;
468 : 18551 : j = i->second.find(did);
469 [ + + ]: 18551 : if (j != i->second.end()) return j->second;
470 : : }
471 : :
472 : : // Read it from the table.
473 : 1668073 : string chunk;
474 : : Xapian::docid first_did;
475 : 1668073 : first_did = get_chunk_containing_did(slot, did, chunk);
476 [ + + ]: 1668071 : if (first_did == 0) return string();
477 : :
478 : 1667061 : ValueChunkReader reader(chunk.data(), chunk.size(), first_did);
479 : 1667061 : reader.skip_to(did);
480 [ + + + + ]: 1667061 : if (reader.at_end() || reader.get_docid() != did) return string();
[ + + ]
481 : 1675969 : return reader.get_value();
482 : : }
483 : :
484 : : void
485 : 172477 : ChertValueManager::get_all_values(map<Xapian::valueno, string> & values,
486 : : Xapian::docid did) const
487 : : {
488 : : Assert(values.empty());
489 [ - + ]: 172477 : if (!termlist_table->is_open())
490 : 0 : throw Xapian::FeatureUnavailableError("Database has no termlist");
491 : 172477 : map<Xapian::docid, string>::const_iterator i = slots.find(did);
492 : 172477 : string s;
493 [ + + ]: 172477 : if (i != slots.end()) {
494 : 8178 : s = i->second;
495 : : } else {
496 : : // Get from table.
497 [ + + ]: 164299 : if (!termlist_table->get_exact_entry(make_slot_key(did), s)) return;
498 : : }
499 : 157780 : const char * p = s.data();
500 : 157780 : const char * end = p + s.size();
501 : 157780 : Xapian::valueno prev_slot = static_cast<Xapian::valueno>(-1);
502 [ + + ]: 1785926 : while (p != end) {
503 : : Xapian::valueno slot;
504 [ - + ]: 1628146 : if (!unpack_uint(&p, end, &slot)) {
505 : 0 : throw Xapian::DatabaseCorruptError("Value slot encoding corrupt");
506 : : }
507 : 1628146 : slot += prev_slot + 1;
508 : 1628146 : prev_slot = slot;
509 : 1628146 : values.insert(make_pair(slot, get_value(did, slot)));
510 : 172477 : }
511 : : }
512 : :
513 : : void
514 : 261 : ChertValueManager::get_value_stats(Xapian::valueno slot) const
515 : : {
516 : : LOGCALL_VOID(DB, "ChertValueManager::get_value_stats", slot);
517 : : // Invalidate the cache first in case an exception is thrown.
518 : 261 : mru_valno = Xapian::BAD_VALUENO;
519 : 261 : get_value_stats(slot, mru_valstats);
520 : 261 : mru_valno = slot;
521 : 261 : }
522 : :
523 : : void
524 : 166425 : ChertValueManager::get_value_stats(Xapian::valueno slot, ValueStats & stats) const
525 : : {
526 : : LOGCALL_VOID(DB, "ChertValueManager::get_value_stats", slot | Literal("[stats]"));
527 : : // Invalidate the cache first in case an exception is thrown.
528 : 166425 : mru_valno = Xapian::BAD_VALUENO;
529 : :
530 : 166425 : string tag;
531 [ + + ]: 166425 : if (postlist_table->get_exact_entry(make_valuestats_key(slot), tag)) {
532 : 347 : const char * pos = tag.data();
533 : 347 : const char * end = pos + tag.size();
534 : :
535 [ - + ]: 347 : if (!unpack_uint(&pos, end, &(stats.freq))) {
536 [ # # ]: 0 : if (*pos == 0) throw Xapian::DatabaseCorruptError("Incomplete stats item in value table");
537 : 0 : throw Xapian::RangeError("Frequency statistic in value table is too large");
538 : : }
539 [ - + ]: 347 : if (!unpack_string(&pos, end, stats.lower_bound)) {
540 [ # # ]: 0 : if (*pos == 0) throw Xapian::DatabaseCorruptError("Incomplete stats item in value table");
541 : 0 : throw Xapian::RangeError("Lower bound in value table is too large");
542 : : }
543 : 347 : size_t len = end - pos;
544 [ + + ]: 347 : if (len == 0) {
545 : 132 : stats.upper_bound = stats.lower_bound;
546 : : } else {
547 : 215 : stats.upper_bound.assign(pos, len);
548 : : }
549 : : } else {
550 : 166078 : stats.clear();
551 : : }
552 : :
553 : 166425 : mru_valno = slot;
554 : 166425 : }
555 : :
556 : : void
557 : 1178 : ChertValueManager::set_value_stats(map<Xapian::valueno, ValueStats> & value_stats)
558 : : {
559 : : LOGCALL_VOID(DB, "ChertValueManager::set_value_stats", value_stats);
560 : 1178 : map<Xapian::valueno, ValueStats>::const_iterator i;
561 [ + + ]: 167341 : for (i = value_stats.begin(); i != value_stats.end(); ++i) {
562 : 166163 : string key = make_valuestats_key(i->first);
563 : 166163 : const ValueStats & stats = i->second;
564 [ + + ]: 166163 : if (stats.freq != 0) {
565 : 166106 : string new_value;
566 : 166106 : pack_uint(new_value, stats.freq);
567 : 166106 : pack_string(new_value, stats.lower_bound);
568 : : // We don't store or count empty values, so neither of the bounds
569 : : // can be empty. So we can safely store an empty upper bound when
570 : : // the bounds are equal.
571 [ + + ]: 166106 : if (stats.lower_bound != stats.upper_bound)
572 : 549 : new_value += stats.upper_bound;
573 : 166106 : postlist_table->add(key, new_value);
574 : : } else {
575 : 57 : postlist_table->del(key);
576 : : }
577 : : }
578 : 1178 : value_stats.clear();
579 : 1178 : mru_valno = Xapian::BAD_VALUENO;
580 : 1178 : }
|