00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <config.h>
00026
00027 #include "chert_table.h"
00028
00029 #include <xapian/error.h>
00030
00031 #include "safeerrno.h"
00032 #ifdef __WIN32__
00033 # include "msvc_posix_wrapper.h"
00034 #endif
00035
00036 #include "omassert.h"
00037 #include "stringutils.h"
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052 #include <sys/types.h>
00053
00054
00055
00056
00057
00058
00059 #if defined HAVE_PREAD && defined PREAD_PROTOTYPE
00060 PREAD_PROTOTYPE
00061 #endif
00062 #if defined HAVE_PWRITE && defined PWRITE_PROTOTYPE
00063 PWRITE_PROTOTYPE
00064 #endif
00065
00066 #include <cstdio>
00067 #include <cstring>
00068 #include <climits>
00069
00070 #include "chert_btreebase.h"
00071 #include "chert_cursor.h"
00072
00073 #include "io_utils.h"
00074 #include "omassert.h"
00075 #include "debuglog.h"
00076 #include "pack.h"
00077 #include "str.h"
00078 #include "unaligned.h"
00079 #include "utils.h"
00080
00081 #include <algorithm>
00082 #include <string>
00083
00084 using namespace std;
00085
00086
00087 const size_t COMPRESS_MIN = 4;
00088
00089
00090 #undef BTREE_DEBUG_FULL
00091
00092 #ifdef BTREE_DEBUG_FULL
00093
00094
00095 static void print_key(const byte * p, int c, int j);
00096 static void print_tag(const byte * p, int c, int j);
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110 #endif
00111
00112 static inline byte *zeroed_new(size_t size)
00113 {
00114 byte *temp = new byte[size];
00115
00116
00117 Assert(temp);
00118 memset(temp, 0, size);
00119 return temp;
00120 }
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00170 #define SEQ_START_POINT (-10)
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185 #define BYTE_PAIR_RANGE (1 << 2 * CHAR_BIT)
00186
00188 void
00189 ChertTable::read_block(uint4 n, byte * p) const
00190 {
00191
00192 LOGCALL_VOID(DB, "ChertTable::read_block", n | (void*)p);
00193
00194
00195
00196 Assert(n / CHAR_BIT < base.get_bit_map_size());
00197
00198 #ifdef HAVE_PREAD
00199 off_t offset = off_t(block_size) * n;
00200 int m = block_size;
00201 while (true) {
00202 ssize_t bytes_read = pread(handle, reinterpret_cast<char *>(p), m,
00203 offset);
00204
00205 if (bytes_read == m) return;
00206 if (bytes_read == -1) {
00207 if (errno == EINTR) continue;
00208 string message = "Error reading block " + str(n) + ": ";
00209 message += strerror(errno);
00210 throw Xapian::DatabaseError(message);
00211 } else if (bytes_read == 0) {
00212 string message = "Error reading block " + str(n) + ": got end of file";
00213 throw Xapian::DatabaseError(message);
00214 } else if (bytes_read < m) {
00215
00216
00217
00218 m -= int(bytes_read);
00219 p += bytes_read;
00220 offset += bytes_read;
00221 }
00222 }
00223 #else
00224 if (lseek(handle, off_t(block_size) * n, SEEK_SET) == -1) {
00225 string message = "Error seeking to block: ";
00226 message += strerror(errno);
00227 throw Xapian::DatabaseError(message);
00228 }
00229
00230 io_read(handle, reinterpret_cast<char *>(p), block_size, block_size);
00231 #endif
00232 }
00233
00240 void
00241 ChertTable::write_block(uint4 n, const byte * p) const
00242 {
00243 LOGCALL_VOID(DB, "ChertTable::write_block", n | p);
00244 Assert(writable);
00245
00246 Assert(n / CHAR_BIT < base.get_bit_map_size());
00247
00248 ;
00249 AssertParanoid(base.block_free_at_start(n));
00250
00251
00252 AssertEqParanoid(REVISION(p), latest_revision_number + 1);
00253
00254 if (both_bases) {
00255
00256
00257
00258
00259
00260
00261
00262 (void)io_unlink(name + "base" + other_base_letter());
00263 both_bases = false;
00264 latest_revision_number = revision_number;
00265 }
00266
00267 #ifdef HAVE_PWRITE
00268 off_t offset = off_t(block_size) * n;
00269 int m = block_size;
00270 while (true) {
00271 ssize_t bytes_written = pwrite(handle, p, m, offset);
00272 if (bytes_written == m) {
00273
00274 return;
00275 } else if (bytes_written == -1) {
00276 if (errno == EINTR) continue;
00277 string message = "Error writing block: ";
00278 message += strerror(errno);
00279 throw Xapian::DatabaseError(message);
00280 } else if (bytes_written == 0) {
00281 string message = "Error writing block: wrote no data";
00282 throw Xapian::DatabaseError(message);
00283 } else if (bytes_written < m) {
00284
00285
00286
00287 m -= bytes_written;
00288 p += bytes_written;
00289 offset += bytes_written;
00290 }
00291 }
00292 #else
00293 if (lseek(handle, (off_t)block_size * n, SEEK_SET) == -1) {
00294 string message = "Error seeking to block: ";
00295 message += strerror(errno);
00296 throw Xapian::DatabaseError(message);
00297 }
00298
00299 io_write(handle, reinterpret_cast<const char *>(p), block_size);
00300 #endif
00301 }
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325 void
00326 ChertTable::set_overwritten() const
00327 {
00328 LOGCALL_VOID(DB, "ChertTable::set_overwritten", NO_ARGS);
00329
00330
00331 if (writable)
00332 throw Xapian::DatabaseCorruptError("Db block overwritten - are there multiple writers?");
00333 throw Xapian::DatabaseModifiedError("The revision being read has been discarded - you should call Xapian::Database::reopen() and retry the operation");
00334 }
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346 void
00347 ChertTable::block_to_cursor(Cursor * C_, int j, uint4 n) const
00348 {
00349 LOGCALL_VOID(DB, "ChertTable::block_to_cursor", (void*)C_ | j | n);
00350 if (n == C_[j].n) return;
00351 byte * p = C_[j].p;
00352 Assert(p);
00353
00354
00355 if (C_[j].rewrite) {
00356 Assert(writable);
00357 Assert(C == C_);
00358 write_block(C_[j].n, p);
00359 C_[j].rewrite = false;
00360 }
00361
00362
00363
00364 if (writable && n == C[j].n) {
00365 if (p != C[j].p)
00366 memcpy(p, C[j].p, block_size);
00367 } else {
00368 read_block(n, p);
00369 }
00370
00371 C_[j].n = n;
00372 if (j < level) {
00373
00374 if (rare(REVISION(p) > REVISION(C_[j + 1].p))) {
00375 set_overwritten();
00376 return;
00377 }
00378 }
00379
00380 if (rare(j != GET_LEVEL(p))) {
00381 string msg = "Expected block ";
00382 msg += str(n);
00383 msg += " to be level ";
00384 msg += str(j);
00385 msg += ", not ";
00386 msg += str(GET_LEVEL(p));
00387 throw Xapian::DatabaseCorruptError(msg);
00388 }
00389 }
00390
00412 void
00413 ChertTable::alter()
00414 {
00415 LOGCALL_VOID(DB, "ChertTable::alter", NO_ARGS);
00416 Assert(writable);
00417 #ifdef DANGEROUS
00418 C[0].rewrite = true;
00419 #else
00420 int j = 0;
00421 byte * p = C[j].p;
00422 while (true) {
00423 if (C[j].rewrite) return;
00424 C[j].rewrite = true;
00425
00426 uint4 n = C[j].n;
00427 if (base.block_free_at_start(n)) {
00428 Assert(REVISION(p) == latest_revision_number + 1);
00429 return;
00430 }
00431 Assert(REVISION(p) < latest_revision_number + 1);
00432 base.free_block(n);
00433 n = base.next_free_block();
00434 C[j].n = n;
00435 SET_REVISION(p, latest_revision_number + 1);
00436
00437 if (j == level) return;
00438 j++;
00439 p = C[j].p;
00440 Item_wr(p, C[j].c).set_block_given_by(n);
00441 }
00442 #endif
00443 }
00444
00457 int
00458 ChertTable::find_in_block(const byte * p, Key key, bool leaf, int c)
00459 {
00460 LOGCALL_STATIC(DB, int, "ChertTable::find_in_block", (void*)p | (const void *)key.get_address() | leaf | c);
00461 int i = DIR_START;
00462 if (leaf) i -= D2;
00463 int j = DIR_END(p);
00464
00465 if (c != -1) {
00466 if (c < j && i < c && Item(p, c).key() <= key)
00467 i = c;
00468 c += D2;
00469 if (c < j && i < c && key < Item(p, c).key())
00470 j = c;
00471 }
00472
00473 while (j - i > D2) {
00474 int k = i + ((j - i)/(D2 * 2))*D2;
00475 if (key < Item(p, k).key()) j = k; else i = k;
00476 }
00477 RETURN(i);
00478 }
00479
00487 bool
00488 ChertTable::find(Cursor * C_) const
00489 {
00490 LOGCALL(DB, bool, "ChertTable::find", (void*)C_);
00491
00492 const byte * p;
00493 int c;
00494 Key key = kt.key();
00495 for (int j = level; j > 0; --j) {
00496 p = C_[j].p;
00497 c = find_in_block(p, key, false, C_[j].c);
00498 #ifdef BTREE_DEBUG_FULL
00499 printf("Block in ChertTable:find - code position 1");
00500 report_block_full(j, C_[j].n, p);
00501 #endif
00502 C_[j].c = c;
00503 block_to_cursor(C_, j - 1, Item(p, c).block_given_by());
00504 }
00505 p = C_[0].p;
00506 c = find_in_block(p, key, true, C_[0].c);
00507 #ifdef BTREE_DEBUG_FULL
00508 printf("Block in ChertTable:find - code position 2");
00509 report_block_full(0, C_[0].n, p);
00510 #endif
00511 C_[0].c = c;
00512 if (c < DIR_START) RETURN(false);
00513 RETURN(Item(p, c).key() == key);
00514 }
00515
00521 void
00522 ChertTable::compact(byte * p)
00523 {
00524 LOGCALL_VOID(DB, "ChertTable::compact", (void*)p);
00525 Assert(writable);
00526 int e = block_size;
00527 byte * b = buffer;
00528 int dir_end = DIR_END(p);
00529 for (int c = DIR_START; c < dir_end; c += D2) {
00530 Item item(p, c);
00531 int l = item.size();
00532 e -= l;
00533 memmove(b + e, item.get_address(), l);
00534 setD(p, c, e);
00535 }
00536 memmove(p + e, b + e, block_size - e);
00537 e -= dir_end;
00538 SET_TOTAL_FREE(p, e);
00539 SET_MAX_FREE(p, e);
00540 }
00541
00545 void
00546 ChertTable::split_root(uint4 split_n)
00547 {
00548 LOGCALL_VOID(DB, "ChertTable::split_root", split_n);
00549
00550 ++level;
00551
00552
00553
00554 if (level == BTREE_CURSOR_LEVELS) {
00555 throw Xapian::DatabaseCorruptError("Btree has grown impossibly large ("STRINGIZE(BTREE_CURSOR_LEVELS)" levels)");
00556 }
00557
00558 byte * q = zeroed_new(block_size);
00559 C[level].p = q;
00560 C[level].c = DIR_START;
00561 C[level].n = base.next_free_block();
00562 C[level].rewrite = true;
00563 SET_REVISION(q, latest_revision_number + 1);
00564 SET_LEVEL(q, level);
00565 SET_DIR_END(q, DIR_START);
00566 compact(q);
00567
00568
00569 byte b[10];
00570 Item_wr item(b);
00571 item.form_null_key(split_n);
00572 add_item(item, level);
00573 }
00574
00590 void
00591 ChertTable::enter_key(int j, Key prevkey, Key newkey)
00592 {
00593 LOGCALL_VOID(DB, "ChertTable::enter_key", j | Literal("prevkey") | Literal("newkey"));
00594 Assert(writable);
00595 Assert(prevkey < newkey);
00596 AssertRel(j,>=,1);
00597
00598 uint4 blocknumber = C[j - 1].n;
00599
00600
00601
00602 const int newkey_len = newkey.length();
00603 AssertRel(newkey_len,>,0);
00604 int i;
00605
00606 if (j == 1) {
00607
00608
00609 i = 0;
00610 const int min_len = min(newkey_len, prevkey.length());
00611 while (i < min_len && prevkey[i] == newkey[i]) {
00612 i++;
00613 }
00614
00615
00616 if (i < newkey_len) i++;
00617 } else {
00618
00619
00620
00621
00622 i = newkey_len;
00623 }
00624
00625 byte b[UCHAR_MAX + 6];
00626 Item_wr item(b);
00627 Assert(i <= 256 - I2 - C2);
00628 Assert(i <= (int)sizeof(b) - I2 - C2 - 4);
00629 item.set_key_and_block(newkey, i, blocknumber);
00630
00631
00632
00633
00634 if (j > 1) {
00635 byte * p = C[j - 1].p;
00636 uint4 n = getint4(newkey.get_address(), newkey_len + K1 + C2);
00637 int new_total_free = TOTAL_FREE(p) + newkey_len + C2;
00638
00639 Item_wr(const_cast<byte*>(newkey.get_address()) - I2).form_null_key(n);
00640 SET_TOTAL_FREE(p, new_total_free);
00641 }
00642
00643 C[j].c = find_in_block(C[j].p, item.key(), false, 0) + D2;
00644 C[j].rewrite = true;
00645 add_item(item, j);
00646 }
00647
00652 int
00653 ChertTable::mid_point(byte * p)
00654 {
00655 LOGCALL(DB, int, "ChertTable::mid_point", (void*)p);
00656 int n = 0;
00657 int dir_end = DIR_END(p);
00658 int size = block_size - TOTAL_FREE(p) - dir_end;
00659 for (int c = DIR_START; c < dir_end; c += D2) {
00660 int l = Item(p, c).size();
00661 n += 2 * l;
00662 if (n >= size) {
00663 if (l < n - size) RETURN(c);
00664 RETURN(c + D2);
00665 }
00666 }
00667
00668
00669 Assert(false);
00670 RETURN(0);
00671 }
00672
00682 void
00683 ChertTable::add_item_to_block(byte * p, Item_wr kt_, int c)
00684 {
00685 LOGCALL_VOID(DB, "ChertTable::add_item_to_block", (void*)p | Literal("kt_") | c);
00686 Assert(writable);
00687 int dir_end = DIR_END(p);
00688 int kt_len = kt_.size();
00689 int needed = kt_len + D2;
00690 int new_total = TOTAL_FREE(p) - needed;
00691 int new_max = MAX_FREE(p) - needed;
00692
00693 Assert(new_total >= 0);
00694
00695 AssertRel(MAX_FREE(p),>=,needed);
00696
00697 Assert(dir_end >= c);
00698
00699 memmove(p + c + D2, p + c, dir_end - c);
00700 dir_end += D2;
00701 SET_DIR_END(p, dir_end);
00702
00703 int o = dir_end + new_max;
00704 setD(p, c, o);
00705 memmove(p + o, kt_.get_address(), kt_len);
00706
00707 SET_MAX_FREE(p, new_max);
00708
00709 SET_TOTAL_FREE(p, new_total);
00710 }
00711
00717 void
00718 ChertTable::add_item(Item_wr kt_, int j)
00719 {
00720 LOGCALL_VOID(DB, "ChertTable::add_item", Literal("kt_") | j);
00721 Assert(writable);
00722 byte * p = C[j].p;
00723 int c = C[j].c;
00724 uint4 n;
00725
00726 int needed = kt_.size() + D2;
00727 if (TOTAL_FREE(p) < needed) {
00728 int m;
00729
00730
00731
00732
00733
00734 if (seq_count < 0) {
00735
00736
00737 m = mid_point(p);
00738 } else {
00739
00740 m = c;
00741 }
00742
00743 uint4 split_n = C[j].n;
00744 C[j].n = base.next_free_block();
00745
00746 memcpy(split_p, p, block_size);
00747 SET_DIR_END(split_p, m);
00748 compact(split_p);
00749
00750 {
00751 int residue = DIR_END(p) - m;
00752 int new_dir_end = DIR_START + residue;
00753 memmove(p + DIR_START, p + m, residue);
00754 SET_DIR_END(p, new_dir_end);
00755 }
00756
00757 compact(p);
00758
00759 bool add_to_upper_half;
00760 if (seq_count < 0) {
00761 add_to_upper_half = (c >= m);
00762 } else {
00763
00764
00765 add_to_upper_half = (TOTAL_FREE(split_p) < needed);
00766 }
00767
00768 if (add_to_upper_half) {
00769 c -= (m - DIR_START);
00770 Assert(seq_count < 0 || c <= DIR_START + D2);
00771 Assert(c >= DIR_START);
00772 Assert(c <= DIR_END(p));
00773 add_item_to_block(p, kt_, c);
00774 n = C[j].n;
00775 } else {
00776 Assert(c >= DIR_START);
00777 Assert(c <= DIR_END(split_p));
00778 add_item_to_block(split_p, kt_, c);
00779 n = split_n;
00780 }
00781 write_block(split_n, split_p);
00782
00783
00784 if (j == level) split_root(split_n);
00785
00786
00787
00788 enter_key(j + 1,
00789 Item(split_p, DIR_END(split_p) - D2).key(),
00790 Item(p, DIR_START).key());
00791 } else {
00792 AssertRel(TOTAL_FREE(p),>=,needed);
00793
00794 if (MAX_FREE(p) < needed) {
00795 compact(p);
00796 AssertRel(MAX_FREE(p),>=,needed);
00797 }
00798
00799 add_item_to_block(p, kt_, c);
00800 n = C[j].n;
00801 }
00802 if (j == 0) {
00803 changed_n = n;
00804 changed_c = c;
00805 }
00806 }
00807
00815 void
00816 ChertTable::delete_item(int j, bool repeatedly)
00817 {
00818 LOGCALL_VOID(DB, "ChertTable::delete_item", j | repeatedly);
00819 Assert(writable);
00820 byte * p = C[j].p;
00821 int c = C[j].c;
00822 int kt_len = Item(p, c).size();
00823 int dir_end = DIR_END(p) - D2;
00824
00825 memmove(p + c, p + c + D2, dir_end - c);
00826 SET_DIR_END(p, dir_end);
00827 SET_MAX_FREE(p, MAX_FREE(p) + D2);
00828 SET_TOTAL_FREE(p, TOTAL_FREE(p) + kt_len + D2);
00829
00830 if (!repeatedly) return;
00831 if (j < level) {
00832 if (dir_end == DIR_START) {
00833 base.free_block(C[j].n);
00834 C[j].rewrite = false;
00835 C[j].n = BLK_UNUSED;
00836 C[j + 1].rewrite = true;
00837 delete_item(j + 1, true);
00838 }
00839 } else {
00840 Assert(j == level);
00841 while (dir_end == DIR_START + D2 && level > 0) {
00842
00843 uint4 new_root = Item(p, DIR_START).block_given_by();
00844 delete [] p;
00845 C[level].p = 0;
00846 base.free_block(C[level].n);
00847 C[level].rewrite = false;
00848 C[level].n = BLK_UNUSED;
00849 level--;
00850
00851 block_to_cursor(C, level, new_root);
00852
00853 p = C[level].p;
00854 dir_end = DIR_END(p);
00855 }
00856 }
00857 }
00858
00859
00860
00861
00862
00888 int
00889 ChertTable::add_kt(bool found)
00890 {
00891 LOGCALL(DB, int, "ChertTable::add_kt", found);
00892 Assert(writable);
00893 int components = 0;
00894
00895
00896
00897
00898
00899
00900
00901 alter();
00902
00903 if (found) {
00904 seq_count = SEQ_START_POINT;
00905 sequential = false;
00906
00907 byte * p = C[0].p;
00908 int c = C[0].c;
00909 Item item(p, c);
00910 int kt_size = kt.size();
00911 int needed = kt_size - item.size();
00912
00913 components = item.components_of();
00914
00915 if (needed <= 0) {
00916
00917 memmove(const_cast<byte *>(item.get_address()),
00918 kt.get_address(), kt_size);
00919 SET_TOTAL_FREE(p, TOTAL_FREE(p) - needed);
00920 } else {
00921
00922 int new_max = MAX_FREE(p) - kt_size;
00923 if (new_max >= 0) {
00924 int o = DIR_END(p) + new_max;
00925 memmove(p + o, kt.get_address(), kt_size);
00926 setD(p, c, o);
00927 SET_MAX_FREE(p, new_max);
00928 SET_TOTAL_FREE(p, TOTAL_FREE(p) - needed);
00929 } else {
00930
00931 delete_item(0, false);
00932 add_item(kt, 0);
00933 }
00934 }
00935 } else {
00936
00937 if (changed_n == C[0].n && changed_c == C[0].c) {
00938 if (seq_count < 0) seq_count++;
00939 } else {
00940 seq_count = SEQ_START_POINT;
00941 sequential = false;
00942 }
00943 C[0].c += D2;
00944 add_item(kt, 0);
00945 }
00946 RETURN(components);
00947 }
00948
00949
00950
00951
00952
00953
00954 int
00955 ChertTable::delete_kt()
00956 {
00957 LOGCALL(DB, int, "ChertTable::delete_kt", NO_ARGS);
00958 Assert(writable);
00959
00960 bool found = find(C);
00961
00962 int components = 0;
00963 seq_count = SEQ_START_POINT;
00964 sequential = false;
00965
00966
00967
00968
00969
00970
00971
00972 if (found) {
00973 components = Item(C[0].p, C[0].c).components_of();
00974 alter();
00975 delete_item(0, true);
00976 }
00977 RETURN(components);
00978 }
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988
00989 void ChertTable::form_key(const string & key) const
00990 {
00991 LOGCALL_VOID(DB, "ChertTable::form_key", key);
00992 kt.form_key(key);
00993 }
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018 void
01019 ChertTable::add(const string &key, string tag, bool already_compressed)
01020 {
01021 LOGCALL_VOID(DB, "ChertTable::add", key | tag | already_compressed);
01022 Assert(writable);
01023
01024 if (handle < 0) create_and_open(block_size);
01025
01026 form_key(key);
01027
01028 bool compressed = false;
01029 if (already_compressed) {
01030 compressed = true;
01031 } else if (compress_strategy != DONT_COMPRESS && tag.size() > COMPRESS_MIN) {
01032 CompileTimeAssert(DONT_COMPRESS != Z_DEFAULT_STRATEGY);
01033 CompileTimeAssert(DONT_COMPRESS != Z_FILTERED);
01034 CompileTimeAssert(DONT_COMPRESS != Z_HUFFMAN_ONLY);
01035 #ifdef Z_RLE
01036 CompileTimeAssert(DONT_COMPRESS != Z_RLE);
01037 #endif
01038
01039 lazy_alloc_deflate_zstream();
01040
01041 deflate_zstream->next_in = (Bytef *)const_cast<char *>(tag.data());
01042 deflate_zstream->avail_in = (uInt)tag.size();
01043
01044
01045 unsigned long blk_len = tag.size() - 1;
01046 unsigned char * blk = new unsigned char[blk_len];
01047 deflate_zstream->next_out = blk;
01048 deflate_zstream->avail_out = (uInt)blk_len;
01049
01050 int err = deflate(deflate_zstream, Z_FINISH);
01051 if (err == Z_STREAM_END) {
01052
01053
01054 tag.assign(reinterpret_cast<const char *>(blk), deflate_zstream->total_out);
01055 compressed = true;
01056 } else {
01057
01058 }
01059
01060 delete [] blk;
01061 }
01062
01063
01064 const size_t cd = kt.key().length() + K1 + I2 + C2 + C2;
01065 const size_t L = max_item_size - cd;
01066 size_t first_L = L;
01067 bool found = find(C);
01068 if (!found) {
01069 byte * p = C[0].p;
01070 size_t n = TOTAL_FREE(p) % (max_item_size + D2);
01071 if (n > D2 + cd) {
01072 n -= (D2 + cd);
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083
01084
01085 size_t last = tag.length() % L;
01086 if (n >= last || (full_compaction && n >= key.size() + 34))
01087 first_L = n;
01088 }
01089 }
01090
01091
01092 int m = tag.empty() ? 1 : (tag.length() - first_L + L - 1) / L + 1;
01093
01094
01095
01096
01097 if (m >= BYTE_PAIR_RANGE)
01098 throw Xapian::UnimplementedError("Can't handle insanely large tags");
01099
01100 int n = 0;
01101
01102 int o = 0;
01103 size_t residue = tag.length();
01104 int replacement = false;
01105 int i;
01106 kt.set_components_of(m);
01107 for (i = 1; i <= m; i++) {
01108 size_t l = (i == m ? residue : (i == 1 ? first_L : L));
01109 Assert(cd + l <= block_size);
01110 Assert(string::size_type(o + l) <= tag.length());
01111 kt.set_tag(cd, tag.data() + o, l, compressed);
01112 kt.set_component_of(i);
01113
01114 o += l;
01115 residue -= l;
01116
01117 if (i > 1) found = find(C);
01118 n = add_kt(found);
01119 if (n > 0) replacement = true;
01120 }
01121
01122 for (i = m + 1; i <= n; i++) {
01123 kt.set_component_of(i);
01124 delete_kt();
01125 }
01126 if (!replacement) ++item_count;
01127 Btree_modified = true;
01128 if (cursor_created_since_last_modification) {
01129 cursor_created_since_last_modification = false;
01130 ++cursor_version;
01131 }
01132 }
01133
01134
01135
01136
01137
01138
01139
01140 bool
01141 ChertTable::del(const string &key)
01142 {
01143 LOGCALL(DB, bool, "ChertTable::del", key);
01144 Assert(writable);
01145
01146 if (handle < 0) {
01147 if (handle == -2) {
01148 ChertTable::throw_database_closed();
01149 }
01150 RETURN(false);
01151 }
01152
01153
01154 if (key.size() > CHERT_BTREE_MAX_KEY_LEN) RETURN(false);
01155
01156 if (key.empty()) RETURN(false);
01157 form_key(key);
01158
01159 int n = delete_kt();
01160 if (n <= 0) RETURN(false);
01161
01162 for (int i = 2; i <= n; i++) {
01163 kt.set_component_of(i);
01164 delete_kt();
01165 }
01166
01167 item_count--;
01168 Btree_modified = true;
01169 if (cursor_created_since_last_modification) {
01170 cursor_created_since_last_modification = false;
01171 ++cursor_version;
01172 }
01173 RETURN(true);
01174 }
01175
01176 bool
01177 ChertTable::get_exact_entry(const string &key, string & tag) const
01178 {
01179 LOGCALL(DB, bool, "ChertTable::get_exact_entry", key | tag);
01180 Assert(!key.empty());
01181
01182 if (handle < 0) {
01183 if (handle == -2) {
01184 ChertTable::throw_database_closed();
01185 }
01186 RETURN(false);
01187 }
01188
01189
01190 if (key.size() > CHERT_BTREE_MAX_KEY_LEN) RETURN(false);
01191
01192 form_key(key);
01193 if (!find(C)) RETURN(false);
01194
01195 (void)read_tag(C, &tag, false);
01196 RETURN(true);
01197 }
01198
01199 bool
01200 ChertTable::key_exists(const string &key) const
01201 {
01202 LOGCALL(DB, bool, "ChertTable::key_exists", key);
01203 Assert(!key.empty());
01204
01205
01206 if (key.size() > CHERT_BTREE_MAX_KEY_LEN) RETURN(false);
01207
01208 form_key(key);
01209 RETURN(find(C));
01210 }
01211
01212 bool
01213 ChertTable::read_tag(Cursor * C_, string *tag, bool keep_compressed) const
01214 {
01215 LOGCALL(DB, bool, "ChertTable::read_tag", Literal("C_") | tag | keep_compressed);
01216 Item item(C_[0].p, C_[0].c);
01217
01218
01219 int n = item.components_of();
01220
01221 tag->resize(0);
01222
01223
01224 if (n > 1) tag->reserve((max_item_size - (1 + K1 + I2 + C2 + C2)) * n);
01225
01226 item.append_chunk(tag);
01227 bool compressed = item.get_compressed();
01228
01229 for (int i = 2; i <= n; i++) {
01230 if (!next(C_, 0)) {
01231 throw Xapian::DatabaseCorruptError("Unexpected end of table when reading continuation of tag");
01232 }
01233 (void)Item(C_[0].p, C_[0].c).append_chunk(tag);
01234 }
01235
01236
01237 if (!compressed || keep_compressed) RETURN(compressed);
01238
01239
01240
01241
01242
01243 string utag;
01244
01245 utag.reserve(tag->size() + tag->size() / 2);
01246
01247 Bytef buf[8192];
01248
01249 lazy_alloc_inflate_zstream();
01250
01251 inflate_zstream->next_in = (Bytef*)const_cast<char *>(tag->data());
01252 inflate_zstream->avail_in = (uInt)tag->size();
01253
01254 int err = Z_OK;
01255 while (err != Z_STREAM_END) {
01256 inflate_zstream->next_out = buf;
01257 inflate_zstream->avail_out = (uInt)sizeof(buf);
01258 err = inflate(inflate_zstream, Z_SYNC_FLUSH);
01259 if (err == Z_BUF_ERROR && inflate_zstream->avail_in == 0) {
01260 LOGLINE(DB, "Z_BUF_ERROR - faking checksum of " << inflate_zstream->adler);
01261 Bytef header2[4];
01262 setint4(header2, 0, inflate_zstream->adler);
01263 inflate_zstream->next_in = header2;
01264 inflate_zstream->avail_in = 4;
01265 err = inflate(inflate_zstream, Z_SYNC_FLUSH);
01266 if (err == Z_STREAM_END) break;
01267 }
01268
01269 if (err != Z_OK && err != Z_STREAM_END) {
01270 if (err == Z_MEM_ERROR) throw std::bad_alloc();
01271 string msg = "inflate failed";
01272 if (inflate_zstream->msg) {
01273 msg += " (";
01274 msg += inflate_zstream->msg;
01275 msg += ')';
01276 }
01277 throw Xapian::DatabaseError(msg);
01278 }
01279
01280 utag.append(reinterpret_cast<const char *>(buf),
01281 inflate_zstream->next_out - buf);
01282 }
01283 if (utag.size() != inflate_zstream->total_out) {
01284 string msg = "compressed tag didn't expand to the expected size: ";
01285 msg += str(utag.size());
01286 msg += " != ";
01287
01288 msg += str((size_t)inflate_zstream->total_out);
01289 throw Xapian::DatabaseCorruptError(msg);
01290 }
01291
01292 swap(*tag, utag);
01293
01294 RETURN(false);
01295 }
01296
01297 void
01298 ChertTable::set_full_compaction(bool parity)
01299 {
01300 LOGCALL_VOID(DB, "ChertTable::set_full_compaction", parity);
01301 Assert(writable);
01302
01303 if (parity) seq_count = 0;
01304 full_compaction = parity;
01305 }
01306
01307 ChertCursor * ChertTable::cursor_get() const {
01308 LOGCALL(DB, ChertCursor *, "ChertTable::cursor_get", NO_ARGS);
01309 if (handle < 0) {
01310 if (handle == -2) {
01311 ChertTable::throw_database_closed();
01312 }
01313 RETURN(NULL);
01314 }
01315
01316 RETURN(new ChertCursor(const_cast<ChertTable *>(this)));
01317 }
01318
01319
01320
01321 bool
01322 ChertTable::basic_open(bool revision_supplied, chert_revision_number_t revision_)
01323 {
01324 LOGCALL(DB, bool, "ChertTable::basic_open", revision_supplied | revision_);
01325 int ch = 'X';
01326
01327 {
01328 const size_t BTREE_BASES = 2;
01329 string err_msg;
01330 static const char basenames[BTREE_BASES] = { 'A', 'B' };
01331
01332 ChertTable_base bases[BTREE_BASES];
01333 bool base_ok[BTREE_BASES];
01334
01335 both_bases = true;
01336 bool valid_base = false;
01337 {
01338 for (size_t i = 0; i < BTREE_BASES; ++i) {
01339 bool ok = bases[i].read(name, basenames[i], writable, err_msg);
01340 base_ok[i] = ok;
01341 if (ok) {
01342 valid_base = true;
01343 } else {
01344 both_bases = false;
01345 }
01346 }
01347 }
01348
01349 if (!valid_base) {
01350 if (handle >= 0) {
01351 ::close(handle);
01352 handle = -1;
01353 }
01354 string message = "Error opening table `";
01355 message += name;
01356 message += "':\n";
01357 message += err_msg;
01358 throw Xapian::DatabaseOpeningError(message);
01359 }
01360
01361 if (revision_supplied) {
01362 bool found_revision = false;
01363 for (size_t i = 0; i < BTREE_BASES; ++i) {
01364 if (base_ok[i] && bases[i].get_revision() == revision_) {
01365 ch = basenames[i];
01366 found_revision = true;
01367 break;
01368 }
01369 }
01370 if (!found_revision) {
01371
01372
01373
01374
01375 RETURN(false);
01376 }
01377 } else {
01378 chert_revision_number_t highest_revision = 0;
01379 for (size_t i = 0; i < BTREE_BASES; ++i) {
01380 if (base_ok[i] && bases[i].get_revision() >= highest_revision) {
01381 ch = basenames[i];
01382 highest_revision = bases[i].get_revision();
01383 }
01384 }
01385 }
01386
01387 ChertTable_base *basep = 0;
01388 ChertTable_base *other_base = 0;
01389
01390 for (size_t i = 0; i < BTREE_BASES; ++i) {
01391 LOGLINE(DB, "Checking (ch == " << ch << ") against "
01392 "basenames[" << i << "] == " << basenames[i]);
01393 LOGLINE(DB, "bases[" << i << "].get_revision() == " <<
01394 bases[i].get_revision());
01395 LOGLINE(DB, "base_ok[" << i << "] == " << base_ok[i]);
01396 if (ch == basenames[i]) {
01397 basep = &bases[i];
01398
01399
01400 size_t otherbase_num = 1-i;
01401 if (base_ok[otherbase_num]) {
01402 other_base = &bases[otherbase_num];
01403 }
01404 break;
01405 }
01406 }
01407 Assert(basep);
01408
01409
01410
01411
01412
01413
01414 base.swap(*basep);
01415
01416 revision_number = base.get_revision();
01417 block_size = base.get_block_size();
01418 root = base.get_root();
01419 level = base.get_level();
01420
01421 item_count = base.get_item_count();
01422 faked_root_block = base.get_have_fakeroot();
01423 sequential = base.get_sequential();
01424
01425 if (other_base != 0) {
01426 latest_revision_number = other_base->get_revision();
01427 if (revision_number > latest_revision_number)
01428 latest_revision_number = revision_number;
01429 } else {
01430 latest_revision_number = revision_number;
01431 }
01432 }
01433
01434
01435 kt = Item_wr(zeroed_new(block_size));
01436
01437 set_max_item_size(BLOCK_CAPACITY);
01438
01439 base_letter = ch;
01440
01441
01442
01443 RETURN(true);
01444 }
01445
01446 void
01447 ChertTable::read_root()
01448 {
01449 LOGCALL_VOID(DB, "ChertTable::read_root", NO_ARGS);
01450 if (faked_root_block) {
01451
01452 byte * p = C[0].p;
01453 Assert(p);
01454
01455
01456
01457
01458 memset(p, 0, block_size);
01459
01460 int o = block_size - I2 - K1 - C2 - C2;
01461 Item_wr(p + o).fake_root_item();
01462
01463 setD(p, DIR_START, o);
01464 SET_DIR_END(p, DIR_START + D2);
01465
01466 o -= (DIR_START + D2);
01467 SET_MAX_FREE(p, o);
01468 SET_TOTAL_FREE(p, o);
01469 SET_LEVEL(p, 0);
01470
01471 if (!writable) {
01472
01473
01474 SET_REVISION(p, 0);
01475 C[0].n = 0;
01476 } else {
01477
01478 SET_REVISION(p, latest_revision_number + 1);
01479 C[0].n = base.next_free_block();
01480 }
01481 } else {
01482
01483 block_to_cursor(C, level, root);
01484
01485 if (REVISION(C[level].p) > revision_number) set_overwritten();
01486
01487 }
01488 }
01489
01490 bool
01491 ChertTable::do_open_to_write(bool revision_supplied,
01492 chert_revision_number_t revision_,
01493 bool create_db)
01494 {
01495 LOGCALL(DB, bool, "ChertTable::do_open_to_write", revision_supplied | revision_ | create_db);
01496 if (handle == -2) {
01497 ChertTable::throw_database_closed();
01498 }
01499 int flags = O_RDWR | O_BINARY;
01500 if (create_db) flags |= O_CREAT | O_TRUNC;
01501 handle = ::open((name + "DB").c_str(), flags, 0666);
01502 if (handle < 0) {
01503
01504
01505 if (lazy && !create_db && errno == ENOENT) {
01506 revision_number = revision_;
01507 RETURN(true);
01508 }
01509 string message(create_db ? "Couldn't create " : "Couldn't open ");
01510 message += name;
01511 message += "DB read/write: ";
01512 message += strerror(errno);
01513 throw Xapian::DatabaseOpeningError(message);
01514 }
01515
01516 if (!basic_open(revision_supplied, revision_)) {
01517 ::close(handle);
01518 handle = -1;
01519 if (!revision_supplied) {
01520 throw Xapian::DatabaseOpeningError("Failed to open for writing");
01521 }
01522
01523
01524
01525 RETURN(false);
01526 }
01527
01528 writable = true;
01529
01530 for (int j = 0; j <= level; j++) {
01531 C[j].n = BLK_UNUSED;
01532 C[j].p = new byte[block_size];
01533 if (C[j].p == 0) {
01534 throw std::bad_alloc();
01535 }
01536 }
01537 split_p = new byte[block_size];
01538 if (split_p == 0) {
01539 throw std::bad_alloc();
01540 }
01541 read_root();
01542
01543 buffer = zeroed_new(block_size);
01544
01545 changed_n = 0;
01546 changed_c = DIR_START;
01547 seq_count = SEQ_START_POINT;
01548
01549 RETURN(true);
01550 }
01551
01552 ChertTable::ChertTable(const char * tablename_, const string & path_,
01553 bool readonly_, int compress_strategy_, bool lazy_)
01554 : tablename(tablename_),
01555 revision_number(0),
01556 item_count(0),
01557 block_size(0),
01558 latest_revision_number(0),
01559 both_bases(false),
01560 base_letter('A'),
01561 faked_root_block(true),
01562 sequential(true),
01563 handle(-1),
01564 level(0),
01565 root(0),
01566 kt(0),
01567 buffer(0),
01568 base(),
01569 name(path_),
01570 seq_count(0),
01571 changed_n(0),
01572 changed_c(0),
01573 max_item_size(0),
01574 Btree_modified(false),
01575 full_compaction(false),
01576 writable(!readonly_),
01577 cursor_created_since_last_modification(false),
01578 cursor_version(0),
01579 split_p(0),
01580 compress_strategy(compress_strategy_),
01581 deflate_zstream(NULL),
01582 inflate_zstream(NULL),
01583 lazy(lazy_)
01584 {
01585 LOGCALL_CTOR(DB, "ChertTable", tablename_ | path_ | readonly_ | compress_strategy_ | lazy_);
01586 }
01587
01588 bool
01589 ChertTable::really_empty() const
01590 {
01591 if (handle < 0) {
01592 if (handle == -2) {
01593 ChertTable::throw_database_closed();
01594 }
01595 return true;
01596 }
01597 ChertCursor cur(const_cast<ChertTable*>(this));
01598 cur.find_entry(string());
01599 return !cur.next();
01600 }
01601
01602 void
01603 ChertTable::lazy_alloc_deflate_zstream() const {
01604 if (usual(deflate_zstream)) {
01605 if (usual(deflateReset(deflate_zstream) == Z_OK)) return;
01606
01607 delete deflate_zstream;
01608 }
01609
01610 deflate_zstream = new z_stream;
01611
01612 deflate_zstream->zalloc = Z_NULL;
01613 deflate_zstream->zfree = Z_NULL;
01614 deflate_zstream->opaque = Z_NULL;
01615
01616
01617
01618 int err;
01619 err = deflateInit2(deflate_zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
01620 -15, 9, compress_strategy);
01621 if (rare(err != Z_OK)) {
01622 if (err == Z_MEM_ERROR) {
01623 delete deflate_zstream;
01624 deflate_zstream = 0;
01625 throw std::bad_alloc();
01626 }
01627 string msg = "deflateInit2 failed (";
01628 if (deflate_zstream->msg) {
01629 msg += deflate_zstream->msg;
01630 } else {
01631 msg += str(err);
01632 }
01633 msg += ')';
01634 delete deflate_zstream;
01635 deflate_zstream = 0;
01636 throw Xapian::DatabaseError(msg);
01637 }
01638 }
01639
01640 void
01641 ChertTable::lazy_alloc_inflate_zstream() const {
01642 if (usual(inflate_zstream)) {
01643 if (usual(inflateReset(inflate_zstream) == Z_OK)) return;
01644
01645 delete inflate_zstream;
01646 }
01647
01648 inflate_zstream = new z_stream;
01649
01650 inflate_zstream->zalloc = Z_NULL;
01651 inflate_zstream->zfree = Z_NULL;
01652 inflate_zstream->opaque = Z_NULL;
01653
01654 inflate_zstream->next_in = Z_NULL;
01655 inflate_zstream->avail_in = 0;
01656
01657 int err = inflateInit2(inflate_zstream, -15);
01658 if (rare(err != Z_OK)) {
01659 if (err == Z_MEM_ERROR) {
01660 delete inflate_zstream;
01661 inflate_zstream = 0;
01662 throw std::bad_alloc();
01663 }
01664 string msg = "inflateInit2 failed (";
01665 if (inflate_zstream->msg) {
01666 msg += inflate_zstream->msg;
01667 } else {
01668 msg += str(err);
01669 }
01670 msg += ')';
01671 delete inflate_zstream;
01672 inflate_zstream = 0;
01673 throw Xapian::DatabaseError(msg);
01674 }
01675 }
01676
01677 bool
01678 ChertTable::exists() const {
01679 LOGCALL(DB, bool, "ChertTable::exists", NO_ARGS);
01680 return (file_exists(name + "DB") &&
01681 (file_exists(name + "baseA") || file_exists(name + "baseB")));
01682 }
01683
01684 void
01685 ChertTable::erase()
01686 {
01687 LOGCALL_VOID(DB, "ChertTable::erase", NO_ARGS);
01688 close();
01689
01690 (void)io_unlink(name + "baseA");
01691 (void)io_unlink(name + "baseB");
01692 (void)io_unlink(name + "DB");
01693 }
01694
01695 void
01696 ChertTable::set_block_size(unsigned int block_size_)
01697 {
01698 LOGCALL_VOID(DB, "ChertTable::set_block_size", block_size_);
01699
01700 if (block_size_ < 2048 || block_size_ > BYTE_PAIR_RANGE ||
01701 (block_size_ & (block_size_ - 1)) != 0) {
01702 block_size_ = CHERT_DEFAULT_BLOCK_SIZE;
01703 }
01704 block_size = block_size_;
01705 }
01706
01707 void
01708 ChertTable::create_and_open(unsigned int block_size_)
01709 {
01710 LOGCALL_VOID(DB, "ChertTable::create_and_open", block_size_);
01711 if (handle == -2) {
01712 ChertTable::throw_database_closed();
01713 }
01714 Assert(writable);
01715 close();
01716
01717 if (block_size_ == 0) abort();
01718 set_block_size(block_size_);
01719
01720
01721
01722
01723
01724
01725
01726
01727 ChertTable_base base_;
01728 base_.set_revision(revision_number);
01729 base_.set_block_size(block_size_);
01730 base_.set_have_fakeroot(true);
01731 base_.set_sequential(true);
01732 base_.write_to_file(name + "baseA", 'A', string(), -1, NULL);
01733
01734
01735 (void)io_unlink(name + "baseB");
01736
01737
01738 (void)do_open_to_write(false, 0, true);
01739 }
01740
01741 ChertTable::~ChertTable() {
01742 LOGCALL_DTOR(DB, "ChertTable");
01743 ChertTable::close();
01744
01745 if (deflate_zstream) {
01746
01747
01748 (void) deflateEnd(deflate_zstream);
01749 delete deflate_zstream;
01750 }
01751
01752 if (inflate_zstream) {
01753
01754
01755 (void) inflateEnd(inflate_zstream);
01756 delete inflate_zstream;
01757 }
01758 }
01759
01760 void ChertTable::close(bool permanent) {
01761 LOGCALL_VOID(DB, "ChertTable::close", NO_ARGS);
01762
01763 if (handle >= 0) {
01764
01765
01766 (void)::close(handle);
01767 handle = -1;
01768 }
01769
01770 if (permanent) {
01771 handle = -2;
01772
01773
01774 return;
01775 }
01776 for (int j = level; j >= 0; j--) {
01777 delete [] C[j].p;
01778 C[j].p = 0;
01779 }
01780 delete [] split_p;
01781 split_p = 0;
01782
01783 delete [] kt.get_address();
01784 kt = 0;
01785 delete [] buffer;
01786 buffer = 0;
01787 }
01788
01789 void
01790 ChertTable::flush_db()
01791 {
01792 LOGCALL_VOID(DB, "ChertTable::flush_db", NO_ARGS);
01793 Assert(writable);
01794 if (handle < 0) {
01795 if (handle == -2) {
01796 ChertTable::throw_database_closed();
01797 }
01798 return;
01799 }
01800
01801 for (int j = level; j >= 0; j--) {
01802 if (C[j].rewrite) {
01803 write_block(C[j].n, C[j].p);
01804 }
01805 }
01806
01807 if (Btree_modified) {
01808 faked_root_block = false;
01809 }
01810 }
01811
01812 void
01813 ChertTable::commit(chert_revision_number_t revision, int changes_fd,
01814 const string * changes_tail)
01815 {
01816 LOGCALL_VOID(DB, "ChertTable::commit", revision | changes_fd | changes_tail);
01817 Assert(writable);
01818
01819 if (revision <= revision_number) {
01820 throw Xapian::DatabaseError("New revision too low");
01821 }
01822
01823 if (handle < 0) {
01824 if (handle == -2) {
01825 ChertTable::throw_database_closed();
01826 }
01827 latest_revision_number = revision_number = revision;
01828 return;
01829 }
01830
01831 try {
01832 if (faked_root_block) {
01833
01834 base.clear_bit_map();
01835 }
01836
01837 base.set_revision(revision);
01838 base.set_root(C[level].n);
01839 base.set_level(level);
01840 base.set_item_count(item_count);
01841 base.set_have_fakeroot(faked_root_block);
01842 base.set_sequential(sequential);
01843
01844 base_letter = other_base_letter();
01845
01846 both_bases = true;
01847 latest_revision_number = revision_number = revision;
01848 root = C[level].n;
01849
01850 Btree_modified = false;
01851
01852 for (int i = 0; i < BTREE_CURSOR_LEVELS; ++i) {
01853 C[i].n = BLK_UNUSED;
01854 C[i].c = -1;
01855 C[i].rewrite = false;
01856 }
01857
01858
01859
01860 string tmp = name;
01861 tmp += "tmp";
01862 string basefile = name;
01863 basefile += "base";
01864 basefile += char(base_letter);
01865 base.write_to_file(tmp, base_letter, tablename, changes_fd, changes_tail);
01866
01867
01868
01869
01870 if (!io_sync(handle)) {
01871 (void)::close(handle);
01872 handle = -1;
01873 (void)unlink(tmp);
01874 throw Xapian::DatabaseError("Can't commit new revision - failed to flush DB to disk");
01875 }
01876
01877 #if defined __WIN32__
01878 if (msvc_posix_rename(tmp.c_str(), basefile.c_str()) < 0)
01879 #else
01880 if (rename(tmp.c_str(), basefile.c_str()) < 0)
01881 #endif
01882 {
01883
01884
01885
01886
01887
01888 int saved_errno = errno;
01889 if (unlink(tmp) == 0 || errno != ENOENT) {
01890 string msg("Couldn't update base file ");
01891 msg += basefile;
01892 msg += ": ";
01893 msg += strerror(saved_errno);
01894 throw Xapian::DatabaseError(msg);
01895 }
01896 }
01897 base.commit();
01898
01899 read_root();
01900
01901 changed_n = 0;
01902 changed_c = DIR_START;
01903 seq_count = SEQ_START_POINT;
01904 } catch (...) {
01905 ChertTable::close();
01906 throw;
01907 }
01908 }
01909
01910 void
01911 ChertTable::write_changed_blocks(int changes_fd)
01912 {
01913 LOGCALL_VOID(DB, "ChertTable::write_changed_blocks", changes_fd);
01914 Assert(changes_fd >= 0);
01915 if (handle < 0) return;
01916 if (faked_root_block) return;
01917
01918 string buf;
01919 pack_uint(buf, 2u);
01920 pack_string(buf, tablename);
01921 pack_uint(buf, block_size);
01922 io_write(changes_fd, buf.data(), buf.size());
01923
01924
01925
01926 uint4 n = 0;
01927 byte * p = new byte[block_size];
01928 try {
01929 base.calculate_last_block();
01930 while (base.find_changed_block(&n)) {
01931 buf.resize(0);
01932 pack_uint(buf, n + 1);
01933 io_write(changes_fd, buf.data(), buf.size());
01934
01935
01936 read_block(n, p);
01937
01938
01939 io_write(changes_fd, reinterpret_cast<const char *>(p),
01940 block_size);
01941 ++n;
01942 }
01943 delete[] p;
01944 p = 0;
01945 } catch (...) {
01946 delete[] p;
01947 throw;
01948 }
01949 buf.resize(0);
01950 pack_uint(buf, 0u);
01951 io_write(changes_fd, buf.data(), buf.size());
01952 }
01953
01954 void
01955 ChertTable::cancel()
01956 {
01957 LOGCALL_VOID(DB, "ChertTable::cancel", NO_ARGS);
01958 Assert(writable);
01959
01960 if (handle < 0) {
01961 if (handle == -2) {
01962 ChertTable::throw_database_closed();
01963 }
01964 latest_revision_number = revision_number;
01965 return;
01966 }
01967
01968
01969
01970 string err_msg;
01971 if (!base.read(name, base_letter, writable, err_msg)) {
01972 throw Xapian::DatabaseCorruptError(string("Couldn't reread base ") + base_letter);
01973 }
01974
01975 revision_number = base.get_revision();
01976 block_size = base.get_block_size();
01977 root = base.get_root();
01978 level = base.get_level();
01979
01980 item_count = base.get_item_count();
01981 faked_root_block = base.get_have_fakeroot();
01982 sequential = base.get_sequential();
01983
01984 latest_revision_number = revision_number;
01985
01986 Btree_modified = false;
01987
01988 for (int j = 0; j <= level; j++) {
01989 C[j].n = BLK_UNUSED;
01990 C[j].rewrite = false;
01991 }
01992 read_root();
01993
01994 changed_n = 0;
01995 changed_c = DIR_START;
01996 seq_count = SEQ_START_POINT;
01997 }
01998
01999
02000
02001 bool
02002 ChertTable::do_open_to_read(bool revision_supplied, chert_revision_number_t revision_)
02003 {
02004 LOGCALL(DB, bool, "ChertTable::do_open_to_read", revision_supplied | revision_);
02005 if (handle == -2) {
02006 ChertTable::throw_database_closed();
02007 }
02008 handle = ::open((name + "DB").c_str(), O_RDONLY | O_BINARY);
02009 if (handle < 0) {
02010 if (lazy) {
02011
02012 revision_number = revision_;
02013 RETURN(true);
02014 }
02015 string message("Couldn't open ");
02016 message += name;
02017 message += "DB to read: ";
02018 message += strerror(errno);
02019 throw Xapian::DatabaseOpeningError(message);
02020 }
02021
02022 if (!basic_open(revision_supplied, revision_)) {
02023 ::close(handle);
02024 handle = -1;
02025 if (revision_supplied) {
02026
02027
02028
02029
02030 RETURN(false);
02031 }
02032 throw Xapian::DatabaseOpeningError("Failed to open table for reading");
02033 }
02034
02035 for (int j = 0; j <= level; j++) {
02036 C[j].n = BLK_UNUSED;
02037 C[j].p = new byte[block_size];
02038 if (C[j].p == 0) {
02039 throw std::bad_alloc();
02040 }
02041 }
02042
02043 read_root();
02044 RETURN(true);
02045 }
02046
02047 void
02048 ChertTable::open()
02049 {
02050 LOGCALL_VOID(DB, "ChertTable::open", NO_ARGS);
02051 LOGLINE(DB, "opening at path " << name);
02052 close();
02053
02054 if (!writable) {
02055
02056 (void)do_open_to_read(false, 0);
02057 return;
02058 }
02059
02060
02061 (void)do_open_to_write(false, 0);
02062 }
02063
02064 bool
02065 ChertTable::open(chert_revision_number_t revision)
02066 {
02067 LOGCALL(DB, bool, "ChertTable::open", revision);
02068 LOGLINE(DB, "opening for particular revision at path " << name);
02069 close();
02070
02071 if (!writable) {
02072 if (do_open_to_read(true, revision)) {
02073 AssertEq(revision_number, revision);
02074 RETURN(true);
02075 } else {
02076 close();
02077 RETURN(false);
02078 }
02079 }
02080
02081 if (!do_open_to_write(true, revision)) {
02082
02083 close();
02084 RETURN(false);
02085 }
02086
02087 AssertEq(revision_number, revision);
02088 RETURN(true);
02089 }
02090
02091 bool
02092 ChertTable::prev_for_sequential(Cursor * C_, int ) const
02093 {
02094 LOGCALL(DB, bool, "ChertTable::prev_for_sequential", Literal("C_") | Literal("/*dummy*/"));
02095 int c = C_[0].c;
02096 if (c == DIR_START) {
02097 byte * p = C_[0].p;
02098 Assert(p);
02099 uint4 n = C_[0].n;
02100 while (true) {
02101 if (n == 0) RETURN(false);
02102 n--;
02103 if (writable) {
02104 if (n == C[0].n) {
02105
02106
02107 memcpy(p, C[0].p, block_size);
02108 } else {
02109
02110
02111
02112
02113
02114 int j;
02115 for (j = 1; j <= level; ++j) {
02116 if (n == C[j].n) break;
02117 }
02118 if (j <= level) continue;
02119
02120
02121
02122
02123 read_block(n, p);
02124 }
02125 } else {
02126 read_block(n, p);
02127 }
02128 if (writable) AssertEq(revision_number, latest_revision_number);
02129 if (REVISION(p) > revision_number + writable) {
02130 set_overwritten();
02131 RETURN(false);
02132 }
02133 if (GET_LEVEL(p) == 0) break;
02134 }
02135 c = DIR_END(p);
02136 C_[0].n = n;
02137 }
02138 c -= D2;
02139 C_[0].c = c;
02140 RETURN(true);
02141 }
02142
02143 bool
02144 ChertTable::next_for_sequential(Cursor * C_, int ) const
02145 {
02146 LOGCALL(DB, bool, "ChertTable::next_for_sequential", Literal("C_") | Literal("/*dummy*/"));
02147 byte * p = C_[0].p;
02148 Assert(p);
02149 int c = C_[0].c;
02150 c += D2;
02151 Assert((unsigned)c < block_size);
02152 if (c == DIR_END(p)) {
02153 uint4 n = C_[0].n;
02154 while (true) {
02155 n++;
02156 if (n > base.get_last_block()) RETURN(false);
02157 if (writable) {
02158 if (n == C[0].n) {
02159
02160
02161 memcpy(p, C[0].p, block_size);
02162 } else {
02163
02164
02165
02166
02167
02168 int j;
02169 for (j = 1; j <= level; ++j) {
02170 if (n == C[j].n) break;
02171 }
02172 if (j <= level) continue;
02173
02174
02175
02176
02177 read_block(n, p);
02178 }
02179 } else {
02180 read_block(n, p);
02181 }
02182 if (writable) AssertEq(revision_number, latest_revision_number);
02183 if (REVISION(p) > revision_number + writable) {
02184 set_overwritten();
02185 RETURN(false);
02186 }
02187 if (GET_LEVEL(p) == 0) break;
02188 }
02189 c = DIR_START;
02190 C_[0].n = n;
02191 }
02192 C_[0].c = c;
02193 RETURN(true);
02194 }
02195
02196 bool
02197 ChertTable::prev_default(Cursor * C_, int j) const
02198 {
02199 LOGCALL(DB, bool, "ChertTable::prev_default", Literal("C_") | j);
02200 byte * p = C_[j].p;
02201 int c = C_[j].c;
02202 Assert(c >= DIR_START);
02203 Assert((unsigned)c < block_size);
02204 Assert(c <= DIR_END(p));
02205 if (c == DIR_START) {
02206 if (j == level) RETURN(false);
02207 if (!prev_default(C_, j + 1)) RETURN(false);
02208 c = DIR_END(p);
02209 }
02210 c -= D2;
02211 C_[j].c = c;
02212 if (j > 0) {
02213 block_to_cursor(C_, j - 1, Item(p, c).block_given_by());
02214 }
02215 RETURN(true);
02216 }
02217
02218 bool
02219 ChertTable::next_default(Cursor * C_, int j) const
02220 {
02221 LOGCALL(DB, bool, "ChertTable::next_default", Literal("C_") | j);
02222 byte * p = C_[j].p;
02223 int c = C_[j].c;
02224 Assert(c >= DIR_START);
02225 c += D2;
02226 Assert((unsigned)c < block_size);
02227
02228 if (c >= DIR_END(p)) {
02229 if (j == level) RETURN(false);
02230 if (!next_default(C_, j + 1)) RETURN(false);
02231 c = DIR_START;
02232 }
02233 C_[j].c = c;
02234 if (j > 0) {
02235 block_to_cursor(C_, j - 1, Item(p, c).block_given_by());
02236 #ifdef BTREE_DEBUG_FULL
02237 printf("Block in ChertTable:next_default");
02238 report_block_full(j - 1, C_[j - 1].n, C_[j - 1].p);
02239 #endif
02240 }
02241 RETURN(true);
02242 }
02243
02244 void
02245 ChertTable::throw_database_closed()
02246 {
02247 throw Xapian::DatabaseError("Database has been closed");
02248 }
02249
02265 bool Key::operator<(Key key2) const
02266 {
02267 LOGCALL(DB, bool, "Key::operator<", static_cast<const void*>(key2.p));
02268 int key1_len = length();
02269 int key2_len = key2.length();
02270 if (key1_len == key2_len) {
02271
02272
02273
02274 RETURN(memcmp(p + K1, key2.p + K1, key1_len + C2) < 0);
02275 }
02276
02277 int k_smaller = (key2_len < key1_len ? key2_len : key1_len);
02278
02279
02280 int diff = memcmp(p + K1, key2.p + K1, k_smaller);
02281 if (diff != 0) RETURN(diff < 0);
02282
02283
02284
02285 RETURN(key1_len < key2_len);
02286 }
02287
02288 bool Key::operator==(Key key2) const
02289 {
02290 LOGCALL(DB, bool, "Key::operator==", static_cast<const void*>(key2.p));
02291 int key1_len = length();
02292 if (key1_len != key2.length()) RETURN(false);
02293
02294
02295
02296 RETURN(memcmp(p + K1, key2.p + K1, key1_len + C2) == 0);
02297 }