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