Branch data Line data Source code
1 : : /** @file xapian-check-flint.cc
2 : : * @brief Check consistency of a flint table.
3 : : */
4 : : /* Copyright 1999,2000,2001 BrightStation PLC
5 : : * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010 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 "xapian-check-flint.h"
26 : :
27 : : #include "bitstream.h"
28 : :
29 : : #include "internaltypes.h"
30 : :
31 : : #include "flint_check.h"
32 : : #include "flint_cursor.h"
33 : : #include "flint_table.h"
34 : : #include "flint_types.h"
35 : : #include "flint_utils.h"
36 : : #include "valuestats.h"
37 : :
38 : : #include <xapian.h>
39 : :
40 : : #include "autoptr.h"
41 : : #include <iostream>
42 : :
43 : : using namespace std;
44 : :
45 : : static inline bool
46 : 0 : is_user_metadata_key(const string & key)
47 : : {
48 [ # # ][ # # ]: 0 : return key.size() > 1 && key[0] == '\0' && key[1] == '\xc0';
[ # # ]
49 : : }
50 : :
51 : : size_t
52 : 4 : check_flint_table(const char * tablename, string filename, int opts,
53 : : vector<Xapian::termcount> & doclens)
54 : : {
55 : 4 : filename += '.';
56 : :
57 : : // Check the btree structure.
58 : 4 : BtreeCheck::check(tablename, filename, opts);
59 : :
60 : : // Now check the flint structures inside the btree.
61 : 4 : FlintTable table(tablename, filename, true);
62 : 4 : table.open();
63 : 4 : AutoPtr<FlintCursor> cursor(table.cursor_get());
64 : :
65 : 4 : size_t errors = 0;
66 : :
67 : 4 : cursor->find_entry("");
68 : 4 : cursor->next(); // Skip the empty entry.
69 : :
70 [ + + ]: 4 : if (strcmp(tablename, "postlist") == 0) {
71 : : // Now check the structure of each postlist in the table.
72 : 1 : string current_term;
73 : 1 : Xapian::docid lastdid = 0;
74 : 1 : Xapian::termcount termfreq = 0, collfreq = 0;
75 : 1 : Xapian::termcount tf = 0, cf = 0;
76 : 1 : bool have_metainfo_key = false;
77 : :
78 : : // The first key/tag pair should be the METAINFO - though this may be
79 : : // missing if the table only contains user-metadata.
80 [ + - ]: 1 : if (!cursor->after_end()) {
81 [ + - ]: 1 : if (cursor->current_key == string("", 1)) {
82 : 1 : have_metainfo_key = true;
83 : 1 : cursor->read_tag();
84 : : // Check format of the METAINFO key.
85 : : Xapian::docid did;
86 : : totlen_t totlen;
87 : 1 : const char * data = cursor->current_tag.data();
88 : 1 : const char * end = data + cursor->current_tag.size();
89 [ - + ]: 1 : if (!F_unpack_uint(&data, end, &did)) {
90 : 0 : cout << "Tag containing meta information is corrupt." << endl;
91 : 0 : ++errors;
92 [ - + ]: 1 : } else if (!F_unpack_uint_last(&data, end, &totlen)) {
93 : 0 : cout << "Tag containing meta information is corrupt." << endl;
94 : 0 : ++errors;
95 [ - + ]: 1 : } else if (data != end) {
96 : 0 : cout << "Tag containing meta information is corrupt." << endl;
97 : 0 : ++errors;
98 : : }
99 : 1 : cursor->next();
100 : : }
101 : : }
102 : :
103 [ - + ]: 1 : for ( ; !cursor->after_end(); cursor->next()) {
104 : 0 : string & key = cursor->current_key;
105 : :
106 [ # # ]: 0 : if (is_user_metadata_key(key)) {
107 : : // User metadata can be anything, so we can't do any particular
108 : : // checks on it other than to check that the tag isn't empty.
109 : 0 : cursor->read_tag();
110 [ # # ]: 0 : if (cursor->current_tag.empty()) {
111 : 0 : cout << "User metadata item is empty" << endl;
112 : 0 : ++errors;
113 : : }
114 : 0 : continue;
115 : : }
116 : :
117 [ # # ]: 0 : if (!have_metainfo_key) {
118 : 0 : cout << "METAINFO key missing from postlist table" << endl;
119 : 0 : ++errors;
120 : : }
121 : :
122 [ # # ][ # # ]: 0 : if (key.size() >= 2 && key[0] == '\0' && key[1] == '\xe0') {
[ # # ][ # # ]
123 : : // doclen chunk
124 : : const char * pos, * end;
125 : 0 : Xapian::docid did = 1;
126 [ # # ]: 0 : if (key.size() > 2) {
127 : : // Non-initial chunk.
128 : 0 : pos = key.data();
129 : 0 : end = pos + key.size();
130 : 0 : pos += 2;
131 [ # # ]: 0 : if (!F_unpack_uint_preserving_sort(&pos, end, &did)) {
132 : 0 : cout << "Error unpacking docid from doclen key" << endl;
133 : 0 : ++errors;
134 : 0 : continue;
135 : : }
136 : : }
137 : :
138 : 0 : cursor->read_tag();
139 : 0 : pos = cursor->current_tag.data();
140 : 0 : end = pos + cursor->current_tag.size();
141 [ # # ]: 0 : if (key.size() == 2) {
142 : : // Initial chunk.
143 [ # # ][ # # ]: 0 : if (end - pos < 2 || pos[0] || pos[1]) {
[ # # ]
144 : 0 : cout << "Initial doclen chunk has nonzero dummy fields" << endl;
145 : 0 : ++errors;
146 : 0 : continue;
147 : : }
148 : 0 : pos += 2;
149 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &did)) {
150 : 0 : cout << "Failed to unpack firstdid for doclen" << endl;
151 : 0 : ++errors;
152 : 0 : continue;
153 : : }
154 : 0 : ++did;
155 [ # # ]: 0 : if (did <= lastdid) {
156 : : cout << "First did in this chunk is <= last in "
157 : 0 : "prev chunk" << endl;
158 : 0 : ++errors;
159 : : }
160 : : }
161 : :
162 : : bool is_last_chunk;
163 [ # # ]: 0 : if (!F_unpack_bool(&pos, end, &is_last_chunk)) {
164 : 0 : cout << "Failed to unpack last chunk flag for doclen" << endl;
165 : 0 : ++errors;
166 : 0 : continue;
167 : : }
168 : : // Read what the final document ID in this chunk is.
169 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &lastdid)) {
170 : 0 : cout << "Failed to unpack increase to last" << endl;
171 : 0 : ++errors;
172 : 0 : continue;
173 : : }
174 : 0 : lastdid += did;
175 : 0 : bool bad = false;
176 : 0 : while (true) {
177 : : Xapian::termcount doclen;
178 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &doclen)) {
179 : 0 : cout << "Failed to unpack doclen" << endl;
180 : 0 : ++errors;
181 : 0 : bad = true;
182 : 0 : break;
183 : : }
184 : :
185 [ # # ]: 0 : if (!doclens.empty()) {
186 [ # # ]: 0 : if (did >= doclens.size()) {
187 : 0 : cout << "document id " << did << " is larger than any in the termlist table!" << endl;
188 : 0 : ++errors;
189 [ # # ]: 0 : } else if (doclens[did] != doclen) {
190 : : cout << "document id " << did << ": length " << doclen
191 : : << " doesn't match " << doclens[did]
192 : 0 : << " in the termlist table" << endl;
193 : 0 : ++errors;
194 : : }
195 : : }
196 : :
197 [ # # ]: 0 : if (pos == end) break;
198 : :
199 : : Xapian::docid inc;
200 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &inc)) {
201 : 0 : cout << "Failed to unpack docid increase" << endl;
202 : 0 : ++errors;
203 : 0 : bad = true;
204 : 0 : break;
205 : : }
206 : 0 : ++inc;
207 : 0 : did += inc;
208 [ # # ]: 0 : if (did > lastdid) {
209 : : cout << "docid " << did << " > last docid " << lastdid
210 : 0 : << endl;
211 : 0 : ++errors;
212 : : }
213 : : }
214 [ # # ]: 0 : if (bad) {
215 : 0 : continue;
216 : : }
217 [ # # ]: 0 : if (is_last_chunk) {
218 [ # # ]: 0 : if (did != lastdid) {
219 : : cout << "lastdid " << lastdid << " != last did " << did
220 : 0 : << endl;
221 : 0 : ++errors;
222 : : }
223 : : }
224 : :
225 : 0 : continue;
226 : : }
227 : :
228 : : const char * pos, * end;
229 : :
230 : : // Get term from key.
231 : 0 : pos = key.data();
232 : 0 : end = pos + key.size();
233 : :
234 : 0 : string term;
235 : 0 : Xapian::docid did = 0;
236 [ # # ]: 0 : if (!F_unpack_string_preserving_sort(&pos, end, term)) {
237 : 0 : cout << "Error unpacking termname from key" << endl;
238 : 0 : ++errors;
239 : 0 : continue;
240 : : }
241 [ # # ]: 0 : if (current_term.empty()) {
242 : 0 : current_term = term;
243 : 0 : tf = cf = 0;
244 [ # # ]: 0 : if (pos != end) {
245 : : cout << "Extra bytes after key for first chunk of "
246 : 0 : "posting list for term `" << term << "'" << endl;
247 : 0 : ++errors;
248 : 0 : continue;
249 : : }
250 : : // Unpack extra header from first chunk.
251 : 0 : cursor->read_tag();
252 : 0 : pos = cursor->current_tag.data();
253 : 0 : end = pos + cursor->current_tag.size();
254 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &termfreq)) {
255 : : cout << "Failed to unpack termfreq for term `" << term
256 : 0 : << "'" << endl;
257 : 0 : ++errors;
258 : 0 : continue;
259 : : }
260 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &collfreq)) {
261 : : cout << "Failed to unpack collfreq for term `" << term
262 : 0 : << "'" << endl;
263 : 0 : ++errors;
264 : 0 : continue;
265 : : }
266 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &did)) {
267 : : cout << "Failed to unpack firstdid for term `" << term
268 : 0 : << "'" << endl;
269 : 0 : ++errors;
270 : 0 : continue;
271 : : }
272 : 0 : ++did;
273 : : } else {
274 [ # # ]: 0 : if (term != current_term) {
275 [ # # ]: 0 : if (pos == end) {
276 : : cout << "No last chunk for term `" << term << "'"
277 : 0 : << endl;
278 : : } else {
279 : : cout << "Mismatch in follow-on chunk in posting "
280 : : "list for term `" << current_term << "' (got `"
281 : 0 : << term << "')" << endl;
282 : : }
283 : 0 : ++errors;
284 : 0 : current_term = term;
285 : : }
286 [ # # ]: 0 : if (pos != end) {
287 [ # # ]: 0 : if (!F_unpack_uint_preserving_sort(&pos, end, &did)) {
288 : 0 : cout << "Failed to unpack did from key" << endl;
289 : 0 : ++errors;
290 : 0 : continue;
291 : : }
292 [ # # ]: 0 : if (did <= lastdid) {
293 : : cout << "First did in this chunk is <= last in "
294 : 0 : "prev chunk" << endl;
295 : 0 : ++errors;
296 : : }
297 : : }
298 : 0 : cursor->read_tag();
299 : 0 : pos = cursor->current_tag.data();
300 : 0 : end = pos + cursor->current_tag.size();
301 : : }
302 : :
303 : : bool is_last_chunk;
304 [ # # ]: 0 : if (!F_unpack_bool(&pos, end, &is_last_chunk)) {
305 : 0 : cout << "Failed to unpack last chunk flag" << endl;
306 : 0 : ++errors;
307 : 0 : continue;
308 : : }
309 : : // Read what the final document ID in this chunk is.
310 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &lastdid)) {
311 : 0 : cout << "Failed to unpack increase to last" << endl;
312 : 0 : ++errors;
313 : 0 : continue;
314 : : }
315 : 0 : ++lastdid;
316 : 0 : lastdid += did;
317 : 0 : bool bad = false;
318 : 0 : while (true) {
319 : : Xapian::termcount wdf;
320 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &wdf)) {
321 : 0 : cout << "Failed to unpack wdf" << endl;
322 : 0 : ++errors;
323 : 0 : bad = true;
324 : 0 : break;
325 : : }
326 : 0 : ++tf;
327 : 0 : cf += wdf;
328 : :
329 : : Xapian::termcount doclen;
330 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &doclen)) {
331 : 0 : cout << "Failed to unpack doc length" << endl;
332 : 0 : ++errors;
333 : 0 : bad = true;
334 : 0 : break;
335 : : }
336 : :
337 [ # # ]: 0 : if (!doclens.empty()) {
338 [ # # ]: 0 : if (did >= doclens.size()) {
339 : 0 : cout << "document id " << did << " is larger than any in the termlist table!" << endl;
340 [ # # ]: 0 : } else if (doclens[did] != doclen) {
341 : 0 : cout << "doclen " << doclen << " doesn't match " << doclens[did] << " in the termlist table" << endl;
342 : 0 : ++errors;
343 : : }
344 : : }
345 [ # # ]: 0 : if (pos == end) break;
346 : :
347 : : Xapian::docid inc;
348 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &inc)) {
349 : 0 : cout << "Failed to unpack docid increase" << endl;
350 : 0 : ++errors;
351 : 0 : bad = true;
352 : 0 : break;
353 : : }
354 : 0 : ++inc;
355 : 0 : did += inc;
356 [ # # ]: 0 : if (did > lastdid) {
357 : : cout << "docid " << did << " > last docid " << lastdid
358 : 0 : << endl;
359 : 0 : ++errors;
360 : : }
361 : : }
362 [ # # ]: 0 : if (bad) {
363 : 0 : continue;
364 : : }
365 [ # # ]: 0 : if (is_last_chunk) {
366 [ # # ]: 0 : if (tf != termfreq) {
367 : : cout << "termfreq " << termfreq << " != # of entries "
368 : 0 : << tf << endl;
369 : 0 : ++errors;
370 : : }
371 [ # # ]: 0 : if (cf != collfreq) {
372 : : cout << "collfreq " << collfreq << " != sum wdf " << cf
373 : 0 : << endl;
374 : 0 : ++errors;
375 : : }
376 [ # # ]: 0 : if (did != lastdid) {
377 : : cout << "lastdid " << lastdid << " != last did " << did
378 : 0 : << endl;
379 : 0 : ++errors;
380 : : }
381 : 0 : current_term.resize(0);
382 : : }
383 : : }
384 [ - + ]: 1 : if (!current_term.empty()) {
385 : : cout << "Last term `" << current_term << "' has no last chunk"
386 : 0 : << endl;
387 : 0 : ++errors;
388 : 1 : }
389 [ + + ]: 3 : } else if (strcmp(tablename, "record") == 0) {
390 : : // Now check the contents of the record table. Any data is valid as
391 : : // the tag so we don't check the tags.
392 [ - + ]: 1 : for ( ; !cursor->after_end(); cursor->next()) {
393 : 0 : string & key = cursor->current_key;
394 : :
395 : : // Get docid from key.
396 : 0 : const char * pos = key.data();
397 : 0 : const char * end = pos + key.size();
398 : :
399 : : Xapian::docid did;
400 [ # # ]: 0 : if (!F_unpack_uint_preserving_sort(&pos, end, &did)) {
401 : 0 : cout << "Error unpacking docid from key" << endl;
402 : 0 : ++errors;
403 [ # # ]: 0 : } else if (pos != end) {
404 : 0 : cout << "Extra junk in key" << endl;
405 : 0 : ++errors;
406 : : }
407 : : }
408 [ + + ]: 2 : } else if (strcmp(tablename, "termlist") == 0) {
409 : : // Now check the contents of the termlist table.
410 [ - + ]: 1 : for ( ; !cursor->after_end(); cursor->next()) {
411 : 0 : string & key = cursor->current_key;
412 : :
413 : : // Get docid from key.
414 : 0 : const char * pos = key.data();
415 : 0 : const char * end = pos + key.size();
416 : :
417 : : Xapian::docid did;
418 [ # # ]: 0 : if (!F_unpack_uint_preserving_sort(&pos, end, &did)) {
419 : 0 : cout << "Error unpacking docid from key" << endl;
420 : 0 : ++errors;
421 : 0 : continue;
422 : : }
423 : :
424 [ # # ]: 0 : if (pos != end) {
425 : 0 : cout << "Extra junk in key" << endl;
426 : 0 : ++errors;
427 : 0 : continue;
428 : : }
429 : :
430 : 0 : cursor->read_tag();
431 : :
432 : 0 : pos = cursor->current_tag.data();
433 : 0 : end = pos + cursor->current_tag.size();
434 : :
435 [ # # ]: 0 : if (pos == end) {
436 : : // Empty termlist.
437 : 0 : continue;
438 : : }
439 : :
440 : : Xapian::termcount doclen, termlist_size;
441 : :
442 : : // Read doclen
443 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &doclen)) {
444 [ # # ]: 0 : if (pos != 0) {
445 : 0 : cout << "doclen out of range" << endl;
446 : : } else {
447 : 0 : cout << "Unexpected end of data when reading doclen" << endl;
448 : : }
449 : 0 : ++errors;
450 : 0 : continue;
451 : : }
452 : :
453 : : // Read termlist_size
454 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &termlist_size)) {
455 [ # # ]: 0 : if (pos != 0) {
456 : 0 : cout << "termlist_size out of range" << endl;
457 : : } else {
458 : 0 : cout << "Unexpected end of data when reading termlist_size" << endl;
459 : : }
460 : 0 : ++errors;
461 : 0 : continue;
462 : : }
463 : :
464 : : // See comment in FlintTermListTable::set_termlist() in
465 : : // flint_termlisttable.cc for an explanation of this!
466 [ # # ][ # # ]: 0 : if (pos != end && *pos == '0') ++pos;
467 : :
468 : 0 : Xapian::termcount actual_doclen = 0, actual_termlist_size = 0;
469 : 0 : string current_tname;
470 : :
471 : 0 : bool bad = false;
472 [ # # ]: 0 : while (pos != end) {
473 : : // Initialize to silence g++ with -O3
474 : 0 : Xapian::doccount current_wdf = 0;
475 : 0 : bool got_wdf = false;
476 : : // If there was a previous term, how much to reuse.
477 [ # # ]: 0 : if (!current_tname.empty()) {
478 : 0 : string::size_type len = static_cast<unsigned char>(*pos++);
479 [ # # ]: 0 : if (len > current_tname.length()) {
480 : : // The wdf was squeezed into the same byte.
481 : 0 : current_wdf = len / (current_tname.length() + 1) - 1;
482 : 0 : len %= (current_tname.length() + 1);
483 : 0 : got_wdf = true;
484 : : }
485 : 0 : current_tname.resize(len);
486 : : }
487 : : // What to append (note len must be positive, since just truncating
488 : : // always takes us backwards in the sort order)
489 : 0 : string::size_type len = static_cast<unsigned char>(*pos++);
490 : 0 : current_tname.append(pos, len);
491 : 0 : pos += len;
492 : :
493 [ # # ]: 0 : if (!got_wdf) {
494 : : // Read wdf
495 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, ¤t_wdf)) {
496 [ # # ]: 0 : if (pos == 0) {
497 : 0 : cout << "Unexpected end of data when reading termlist current_wdf" << endl;
498 : : } else {
499 : 0 : cout << "Size of wdf out of range, in termlist" << endl;
500 : : }
501 : 0 : ++errors;
502 : 0 : bad = true;
503 : 0 : break;
504 : : }
505 : : }
506 : :
507 : 0 : ++actual_termlist_size;
508 : 0 : actual_doclen += current_wdf;
509 : : }
510 [ # # ]: 0 : if (bad) {
511 : 0 : continue;
512 : : }
513 : :
514 [ # # ]: 0 : if (termlist_size != actual_termlist_size) {
515 : 0 : cout << "termlist_size != # of entries in termlist" << endl;
516 : 0 : ++errors;
517 : : }
518 [ # # ]: 0 : if (doclen != actual_doclen) {
519 : 0 : cout << "doclen != sum(wdf)" << endl;
520 : 0 : ++errors;
521 : : }
522 : :
523 : : // + 1 so that did is a valid subscript.
524 [ # # ]: 0 : if (doclens.size() <= did) doclens.resize(did + 1);
525 : 0 : doclens[did] = actual_doclen;
526 : : }
527 [ + - ]: 1 : } else if (strcmp(tablename, "value") == 0) {
528 : : // Now check the contents of the value table.
529 [ - + ]: 1 : for ( ; !cursor->after_end(); cursor->next()) {
530 : 0 : string & key = cursor->current_key;
531 : :
532 : : // Get docid from key.
533 : 0 : const char * pos = key.data();
534 : 0 : const char * end = pos + key.size();
535 : :
536 : : Xapian::docid did;
537 [ # # ]: 0 : if (!F_unpack_uint_preserving_sort(&pos, end, &did)) {
538 : 0 : cout << "Error unpacking docid from key" << endl;
539 : 0 : ++errors;
540 [ # # ]: 0 : } else if (pos != end) {
541 : 0 : cout << "Extra junk in key" << endl;
542 : 0 : ++errors;
543 : : }
544 : :
545 : 0 : cursor->read_tag();
546 : :
547 : 0 : pos = cursor->current_tag.data();
548 : 0 : end = pos + cursor->current_tag.size();
549 : :
550 : 0 : bool first = true;
551 : 0 : Xapian::valueno last_value_no = 0;
552 [ # # ][ # # ]: 0 : while (pos && pos != end) {
[ # # ][ # # ]
553 : : Xapian::valueno this_value_no;
554 : 0 : string this_value;
555 : :
556 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &this_value_no)) {
557 [ # # ]: 0 : if (pos == 0)
558 : 0 : cout << "Incomplete item in value table" << endl;
559 : : else
560 : 0 : cout << "Value number in value table is too large" << endl;
561 : 0 : ++errors;
562 : : break;
563 : : }
564 : :
565 [ # # ]: 0 : if (!F_unpack_string(&pos, end, this_value)) {
566 [ # # ]: 0 : if (pos == 0)
567 : 0 : cout << "Incomplete item in value table" << endl;
568 : : else
569 : 0 : cout << "Item in value table is too large" << endl;
570 : 0 : ++errors;
571 : : break;
572 : : }
573 : :
574 [ # # ]: 0 : if (first) {
575 : 0 : first = false;
576 [ # # ]: 0 : } else if (this_value_no <= last_value_no) {
577 : 0 : cout << "Values not in sorted order - valueno " << last_value_no << " comes before valueno " << this_value_no << endl;
578 : 0 : ++errors;
579 : : }
580 : 0 : last_value_no = this_value_no;
581 : : }
582 : : }
583 [ # # ]: 0 : } else if (strcmp(tablename, "position") == 0) {
584 : : // Now check the contents of the position table.
585 [ # # ]: 0 : for ( ; !cursor->after_end(); cursor->next()) {
586 : 0 : string & key = cursor->current_key;
587 : :
588 : : // Get docid from key.
589 : 0 : const char * pos = key.data();
590 : 0 : const char * end = pos + key.size();
591 : :
592 : : Xapian::docid did;
593 [ # # ]: 0 : if (!F_unpack_uint_preserving_sort(&pos, end, &did)) {
594 : 0 : cout << "Error unpacking docid from key" << endl;
595 : 0 : ++errors;
596 : 0 : continue;
597 : : }
598 [ # # ]: 0 : if (pos == end) {
599 : 0 : cout << "No termname in key" << endl;
600 : 0 : ++errors;
601 : 0 : continue;
602 : : }
603 : :
604 : 0 : cursor->read_tag();
605 : :
606 : 0 : const string & data = cursor->current_tag;
607 : 0 : pos = data.data();
608 : 0 : end = pos + data.size();
609 : :
610 : : Xapian::termpos pos_last;
611 [ # # ]: 0 : if (!F_unpack_uint(&pos, end, &pos_last)) {
612 : 0 : cout << tablename << " table: Position list data corrupt" << endl;
613 : 0 : ++errors;
614 : 0 : continue;
615 : : }
616 [ # # ]: 0 : if (pos == end) {
617 : : // Special case for single entry position list.
618 : : } else {
619 : : // Skip the header we just read.
620 : 0 : BitReader rd(data, pos - data.data());
621 : 0 : Xapian::termpos pos_first = rd.decode(pos_last);
622 : 0 : Xapian::termpos pos_size = rd.decode(pos_last - pos_first) + 2;
623 : 0 : vector<Xapian::termpos> positions;
624 : 0 : positions.resize(pos_size);
625 : 0 : positions[0] = pos_first;
626 : 0 : positions.back() = pos_last;
627 : 0 : rd.decode_interpolative(positions, 0, pos_size - 1);
628 : 0 : vector<Xapian::termpos>::const_iterator current_pos = positions.begin();
629 : 0 : Xapian::termpos lastpos = *current_pos++;
630 [ # # ]: 0 : while (current_pos != positions.end()) {
631 : 0 : Xapian::termpos termpos = *current_pos++;
632 [ # # ]: 0 : if (termpos <= lastpos) {
633 : 0 : cout << tablename << " table: Positions not strictly monotonically increasing" << endl;
634 : 0 : ++errors;
635 : 0 : break;
636 : : }
637 : 0 : lastpos = termpos;
638 : 0 : }
639 : : }
640 : : }
641 : : } else {
642 : 0 : cout << tablename << " table: Don't know how to check structure\n" << endl;
643 : 0 : return errors;
644 : : }
645 : :
646 [ + - ]: 4 : if (!errors)
647 : 4 : cout << tablename << " table structure checked OK\n" << endl;
648 : : else
649 : 0 : cout << tablename << " table errors found: " << errors << "\n" << endl;
650 : :
651 : 4 : return errors;
652 [ + - ][ + - ]: 15 : }
|