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
00026
00027
00028 #include <config.h>
00029
00030 #include "brass_database.h"
00031
00032 #include <xapian/error.h>
00033 #include <xapian/valueiterator.h>
00034
00035 #include "contiguousalldocspostlist.h"
00036 #include "brass_alldocspostlist.h"
00037 #include "brass_alltermslist.h"
00038 #include "brass_replicate_internal.h"
00039 #include "brass_document.h"
00040 #include "../flint_lock.h"
00041 #include "brass_metadata.h"
00042 #include "brass_positionlist.h"
00043 #include "brass_postlist.h"
00044 #include "brass_record.h"
00045 #include "brass_spellingwordslist.h"
00046 #include "brass_termlist.h"
00047 #include "brass_valuelist.h"
00048 #include "brass_values.h"
00049 #include "debuglog.h"
00050 #include "io_utils.h"
00051 #include "pack.h"
00052 #include "remoteconnection.h"
00053 #include "replication.h"
00054 #include "replicationprotocol.h"
00055 #include "serialise.h"
00056 #include "str.h"
00057 #include "stringutils.h"
00058 #include "utils.h"
00059 #include "valuestats.h"
00060
00061 #ifdef __WIN32__
00062 # include "msvc_posix_wrapper.h"
00063 #endif
00064
00065 #include "safeerrno.h"
00066 #include "safesysstat.h"
00067 #include <sys/types.h>
00068
00069 #include <algorithm>
00070 #include "autoptr.h"
00071 #include <string>
00072
00073 using namespace std;
00074 using namespace Xapian;
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088 #define MAX_SAFE_TERM_LENGTH 245
00089
00096 const int MAX_OPEN_RETRIES = 100;
00097
00098
00099
00100
00101
00102 BrassDatabase::BrassDatabase(const string &brass_dir, int action,
00103 unsigned int block_size)
00104 : db_dir(brass_dir),
00105 readonly(action == XAPIAN_DB_READONLY),
00106 version_file(db_dir),
00107 postlist_table(db_dir, readonly),
00108 position_table(db_dir, readonly),
00109 termlist_table(db_dir, readonly),
00110 value_manager(&postlist_table, &termlist_table),
00111 synonym_table(db_dir, readonly),
00112 spelling_table(db_dir, readonly),
00113 record_table(db_dir, readonly),
00114 lock(db_dir),
00115 max_changesets(0)
00116 {
00117 LOGCALL_CTOR(DB, "BrassDatabase", brass_dir | action | block_size);
00118
00119 if (action == XAPIAN_DB_READONLY) {
00120 open_tables_consistent();
00121 return;
00122 }
00123
00124 if (action != Xapian::DB_OPEN && !database_exists()) {
00125
00126
00127
00128 bool fail = false;
00129 struct stat statbuf;
00130 if (stat(db_dir, &statbuf) == 0) {
00131 if (!S_ISDIR(statbuf.st_mode)) fail = true;
00132 } else if (errno != ENOENT || mkdir(db_dir, 0755) == -1) {
00133 fail = true;
00134 }
00135 if (fail) {
00136 throw Xapian::DatabaseCreateError("Cannot create directory `" +
00137 db_dir + "'", errno);
00138 }
00139 get_database_write_lock(true);
00140
00141 create_and_open_tables(block_size);
00142 return;
00143 }
00144
00145 if (action == Xapian::DB_CREATE) {
00146 throw Xapian::DatabaseCreateError("Can't create new database at `" +
00147 db_dir + "': a database already exists and I was told "
00148 "not to overwrite it");
00149 }
00150
00151 get_database_write_lock(false);
00152
00153 if (action == Xapian::DB_CREATE_OR_OVERWRITE) {
00154 create_and_open_tables(block_size);
00155 return;
00156 }
00157
00158
00159 open_tables_consistent();
00160
00161
00162
00163
00164 if (record_table.get_open_revision_number() !=
00165 postlist_table.get_latest_revision_number()) {
00166 brass_revision_number_t new_revision = get_next_revision_number();
00167
00168 set_revision_number(new_revision);
00169 }
00170 }
00171
00172 BrassDatabase::~BrassDatabase()
00173 {
00174 LOGCALL_DTOR(DB, "~BrassDatabase");
00175 }
00176
00177 bool
00178 BrassDatabase::database_exists() {
00179 LOGCALL(DB, bool, "BrassDatabase::database_exists", NO_ARGS);
00180 RETURN(record_table.exists() && postlist_table.exists());
00181 }
00182
00183 void
00184 BrassDatabase::create_and_open_tables(unsigned int block_size)
00185 {
00186 LOGCALL_VOID(DB, "BrassDatabase::create_and_open_tables", NO_ARGS);
00187
00188
00189
00190
00191
00192 version_file.create();
00193 postlist_table.create_and_open(block_size);
00194 position_table.create_and_open(block_size);
00195 termlist_table.create_and_open(block_size);
00196 synonym_table.create_and_open(block_size);
00197 spelling_table.create_and_open(block_size);
00198 record_table.create_and_open(block_size);
00199
00200 Assert(database_exists());
00201
00202
00203 brass_revision_number_t revision = record_table.get_open_revision_number();
00204 if (revision != postlist_table.get_open_revision_number()) {
00205 throw Xapian::DatabaseCreateError("Newly created tables are not in consistent state");
00206 }
00207
00208 stats.zero();
00209 }
00210
00211 void
00212 BrassDatabase::open_tables_consistent()
00213 {
00214 LOGCALL_VOID(DB, "BrassDatabase::open_tables_consistent", NO_ARGS);
00215
00216
00217
00218
00219
00220
00221
00222
00223 brass_revision_number_t cur_rev = record_table.get_open_revision_number();
00224
00225
00226 if (cur_rev == 0) version_file.read_and_check();
00227
00228 record_table.open();
00229 brass_revision_number_t revision = record_table.get_open_revision_number();
00230
00231 if (cur_rev && cur_rev == revision) {
00232
00233
00234 return;
00235 }
00236
00237
00238 unsigned int block_size = record_table.get_block_size();
00239 position_table.set_block_size(block_size);
00240 termlist_table.set_block_size(block_size);
00241 synonym_table.set_block_size(block_size);
00242 spelling_table.set_block_size(block_size);
00243
00244 value_manager.reset();
00245
00246 bool fully_opened = false;
00247 int tries_left = MAX_OPEN_RETRIES;
00248 while (!fully_opened && (tries_left--) > 0) {
00249 if (spelling_table.open(revision) &&
00250 synonym_table.open(revision) &&
00251 termlist_table.open(revision) &&
00252 position_table.open(revision) &&
00253 postlist_table.open(revision)) {
00254
00255 fully_opened = true;
00256 } else {
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267 record_table.open();
00268 brass_revision_number_t newrevision =
00269 record_table.get_open_revision_number();
00270 if (revision == newrevision) {
00271
00272
00273
00274 throw Xapian::DatabaseCorruptError("Cannot open tables at consistent revisions");
00275 }
00276 revision = newrevision;
00277 }
00278 }
00279
00280 if (!fully_opened) {
00281 throw Xapian::DatabaseModifiedError("Cannot open tables at stable revision - changing too fast");
00282 }
00283
00284 stats.read(postlist_table);
00285 }
00286
00287 void
00288 BrassDatabase::open_tables(brass_revision_number_t revision)
00289 {
00290 LOGCALL_VOID(DB, "BrassDatabase::open_tables", revision);
00291 version_file.read_and_check();
00292 record_table.open(revision);
00293
00294
00295 unsigned int block_size = record_table.get_block_size();
00296 position_table.set_block_size(block_size);
00297 termlist_table.set_block_size(block_size);
00298 synonym_table.set_block_size(block_size);
00299 spelling_table.set_block_size(block_size);
00300
00301 value_manager.reset();
00302
00303 spelling_table.open(revision);
00304 synonym_table.open(revision);
00305 termlist_table.open(revision);
00306 position_table.open(revision);
00307 postlist_table.open(revision);
00308 }
00309
00310 brass_revision_number_t
00311 BrassDatabase::get_revision_number() const
00312 {
00313 LOGCALL(DB, brass_revision_number_t, "BrassDatabase::get_revision_number", NO_ARGS);
00314
00315 RETURN(postlist_table.get_open_revision_number());
00316 }
00317
00318 brass_revision_number_t
00319 BrassDatabase::get_next_revision_number() const
00320 {
00321 LOGCALL(DB, brass_revision_number_t, "BrassDatabase::get_next_revision_number", NO_ARGS);
00322
00323
00324
00325
00326 brass_revision_number_t new_revision =
00327 postlist_table.get_latest_revision_number();
00328 ++new_revision;
00329 RETURN(new_revision);
00330 }
00331
00332 void
00333 BrassDatabase::get_changeset_revisions(const string & path,
00334 brass_revision_number_t * startrev,
00335 brass_revision_number_t * endrev) const
00336 {
00337 int changes_fd = -1;
00338 #ifdef __WIN32__
00339 changes_fd = msvc_posix_open(path.c_str(), O_RDONLY);
00340 #else
00341 changes_fd = open(path.c_str(), O_RDONLY);
00342 #endif
00343 fdcloser closer(changes_fd);
00344
00345 if (changes_fd < 0) {
00346 string message = string("Couldn't open changeset ")
00347 + path + " to read";
00348 throw Xapian::DatabaseError(message, errno);
00349 }
00350
00351 char buf[REASONABLE_CHANGESET_SIZE];
00352 const char *start = buf;
00353 const char *end = buf + io_read(changes_fd, buf,
00354 REASONABLE_CHANGESET_SIZE, 0);
00355 if (strncmp(start, CHANGES_MAGIC_STRING,
00356 CONST_STRLEN(CHANGES_MAGIC_STRING)) != 0) {
00357 string message = string("Changeset at ")
00358 + path + " does not contain valid magic string";
00359 throw Xapian::DatabaseError(message);
00360 }
00361 start += CONST_STRLEN(CHANGES_MAGIC_STRING);
00362 if (start >= end)
00363 throw Xapian::DatabaseError("Changeset too short at " + path);
00364
00365 unsigned int changes_version;
00366 if (!unpack_uint(&start, end, &changes_version))
00367 throw Xapian::DatabaseError("Couldn't read a valid version number for "
00368 "changeset at " + path);
00369 if (changes_version != CHANGES_VERSION)
00370 throw Xapian::DatabaseError("Don't support version of changeset at "
00371 + path);
00372
00373 if (!unpack_uint(&start, end, startrev))
00374 throw Xapian::DatabaseError("Couldn't read a valid start revision from "
00375 "changeset at " + path);
00376
00377 if (!unpack_uint(&start, end, endrev))
00378 throw Xapian::DatabaseError("Couldn't read a valid end revision for "
00379 "changeset at " + path);
00380 }
00381
00382 void
00383 BrassDatabase::set_revision_number(brass_revision_number_t new_revision)
00384 {
00385 LOGCALL_VOID(DB, "BrassDatabase::set_revision_number", new_revision);
00386
00387 value_manager.merge_changes();
00388
00389 postlist_table.flush_db();
00390 position_table.flush_db();
00391 termlist_table.flush_db();
00392 synonym_table.flush_db();
00393 spelling_table.flush_db();
00394 record_table.flush_db();
00395
00396 int changes_fd = -1;
00397 string changes_name;
00398
00399
00400 const char *p = getenv("XAPIAN_MAX_CHANGESETS");
00401 if (p) {
00402 max_changesets = atoi(p);
00403 } else {
00404 max_changesets = 0;
00405 }
00406
00407 if (max_changesets > 0) {
00408 brass_revision_number_t old_revision = get_revision_number();
00409 if (old_revision) {
00410
00411 changes_name = db_dir + "/changes" + str(old_revision);
00412 #ifdef __WIN32__
00413 changes_fd = msvc_posix_open(changes_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
00414 #else
00415 changes_fd = open(changes_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
00416 #endif
00417 if (changes_fd < 0) {
00418 string message = string("Couldn't open changeset ")
00419 + changes_name + " to write";
00420 throw Xapian::DatabaseError(message, errno);
00421 }
00422 }
00423 }
00424
00425 try {
00426 fdcloser closefd(changes_fd);
00427 if (changes_fd >= 0) {
00428 string buf;
00429 brass_revision_number_t old_revision = get_revision_number();
00430 buf += CHANGES_MAGIC_STRING;
00431 pack_uint(buf, CHANGES_VERSION);
00432 pack_uint(buf, old_revision);
00433 pack_uint(buf, new_revision);
00434
00435 #ifndef DANGEROUS
00436 buf += '\x00';
00437 #else
00438 buf += '\x01';
00439 #endif
00440
00441 io_write(changes_fd, buf.data(), buf.size());
00442
00443
00444
00445
00446
00447 termlist_table.write_changed_blocks(changes_fd);
00448 synonym_table.write_changed_blocks(changes_fd);
00449 spelling_table.write_changed_blocks(changes_fd);
00450 record_table.write_changed_blocks(changes_fd);
00451 position_table.write_changed_blocks(changes_fd);
00452 postlist_table.write_changed_blocks(changes_fd);
00453 }
00454
00455 postlist_table.commit(new_revision, changes_fd);
00456 position_table.commit(new_revision, changes_fd);
00457 termlist_table.commit(new_revision, changes_fd);
00458 synonym_table.commit(new_revision, changes_fd);
00459 spelling_table.commit(new_revision, changes_fd);
00460
00461 string changes_tail;
00462 if (changes_fd >= 0) {
00463 changes_tail += '\0';
00464 pack_uint(changes_tail, new_revision);
00465 }
00466 record_table.commit(new_revision, changes_fd, &changes_tail);
00467
00468 } catch (...) {
00469
00470 if (changes_fd >= 0) {
00471 (void)io_unlink(changes_name);
00472 }
00473
00474 throw;
00475 }
00476
00477
00478
00479 if (changes_fd >= 0 && max_changesets < new_revision) {
00480
00481
00482
00483 brass_revision_number_t oldest_changeset = stats.get_oldest_changeset();
00484 brass_revision_number_t stop_changeset = new_revision - max_changesets;
00485 while (oldest_changeset < stop_changeset) {
00486 if (io_unlink(db_dir + "/changes" + str(oldest_changeset))) {
00487 LOGLINE(DB, "Removed changeset " << oldest_changeset);
00488 } else {
00489 LOGLINE(DB, "Skipping changeset " << oldest_changeset <<
00490 ", likely removed before");
00491 }
00492 stats.set_oldest_changeset(oldest_changeset++);
00493 }
00494 }
00495 }
00496
00497 void
00498 BrassDatabase::reopen()
00499 {
00500 LOGCALL_VOID(DB, "BrassDatabase::reopen", NO_ARGS);
00501 if (readonly) open_tables_consistent();
00502 }
00503
00504 void
00505 BrassDatabase::close()
00506 {
00507 LOGCALL_VOID(DB, "BrassDatabase::close", NO_ARGS);
00508 postlist_table.close(true);
00509 position_table.close(true);
00510 termlist_table.close(true);
00511 synonym_table.close(true);
00512 spelling_table.close(true);
00513 record_table.close(true);
00514 lock.release();
00515 }
00516
00517 void
00518 BrassDatabase::get_database_write_lock(bool creating)
00519 {
00520 LOGCALL_VOID(DB, "BrassDatabase::get_database_write_lock", creating);
00521 string explanation;
00522 FlintLock::reason why = lock.lock(true, explanation);
00523 if (why != FlintLock::SUCCESS) {
00524 if (why == FlintLock::UNKNOWN && !creating && !database_exists()) {
00525 string msg("No brass database found at path `");
00526 msg += db_dir;
00527 msg += '\'';
00528 throw Xapian::DatabaseOpeningError(msg);
00529 }
00530 lock.throw_databaselockerror(why, db_dir, explanation);
00531 }
00532 }
00533
00534 void
00535 BrassDatabase::send_whole_database(RemoteConnection & conn, double end_time)
00536 {
00537 LOGCALL_VOID(DB, "BrassDatabase::send_whole_database", conn | end_time);
00538
00539
00540 string buf;
00541 string uuid = get_uuid();
00542 buf += encode_length(uuid.size());
00543 buf += uuid;
00544 pack_uint(buf, get_revision_number());
00545 conn.send_message(REPL_REPLY_DB_HEADER, buf, end_time);
00546
00547
00548
00549 static const char filenames[] =
00550 "\x0b""termlist.DB""\x0e""termlist.baseA\x0e""termlist.baseB"
00551 "\x0a""synonym.DB""\x0d""synonym.baseA\x0d""synonym.baseB"
00552 "\x0b""spelling.DB""\x0e""spelling.baseA\x0e""spelling.baseB"
00553 "\x09""record.DB""\x0c""record.baseA\x0c""record.baseB"
00554 "\x0b""position.DB""\x0e""position.baseA\x0e""position.baseB"
00555 "\x0b""postlist.DB""\x0e""postlist.baseA\x0e""postlist.baseB"
00556 "\x08""iambrass";
00557 string filepath = db_dir;
00558 filepath += '/';
00559 for (const char * p = filenames; *p; p += *p + 1) {
00560 string leaf(p + 1, size_t(static_cast<unsigned char>(*p)));
00561 filepath.replace(db_dir.size() + 1, string::npos, leaf);
00562 #ifdef __WIN32__
00563 int fd = msvc_posix_open(filepath.c_str(), O_RDONLY);
00564 #else
00565 int fd = open(filepath.c_str(), O_RDONLY);
00566 #endif
00567 if (fd > 0) {
00568 fdcloser closefd(fd);
00569 conn.send_message(REPL_REPLY_DB_FILENAME, leaf, end_time);
00570 conn.send_file(REPL_REPLY_DB_FILEDATA, fd, end_time);
00571 }
00572 }
00573 }
00574
00575 void
00576 BrassDatabase::write_changesets_to_fd(int fd,
00577 const string & revision,
00578 bool need_whole_db,
00579 ReplicationInfo * info)
00580 {
00581 LOGCALL_VOID(DB, "BrassDatabase::write_changesets_to_fd", fd | revision | need_whole_db | info);
00582
00583 int whole_db_copies_left = MAX_DB_COPIES_PER_CONVERSATION;
00584 brass_revision_number_t start_rev_num = 0;
00585 string start_uuid = get_uuid();
00586
00587 brass_revision_number_t needed_rev_num = 0;
00588
00589 const char * rev_ptr = revision.data();
00590 const char * rev_end = rev_ptr + revision.size();
00591 if (!unpack_uint(&rev_ptr, rev_end, &start_rev_num)) {
00592 need_whole_db = true;
00593 }
00594
00595 RemoteConnection conn(-1, fd, string());
00596
00597
00598
00599
00600
00601
00602
00603 while (true) {
00604 if (need_whole_db) {
00605
00606
00607
00608 if (whole_db_copies_left == 0) {
00609 conn.send_message(REPL_REPLY_FAIL,
00610 "Database changing too fast",
00611 0.0);
00612 return;
00613 }
00614 whole_db_copies_left--;
00615
00616
00617 start_rev_num = get_revision_number();
00618 start_uuid = get_uuid();
00619
00620 send_whole_database(conn, 0.0);
00621 if (info != NULL)
00622 ++(info->fullcopy_count);
00623
00624 need_whole_db = false;
00625
00626 reopen();
00627 if (start_uuid == get_uuid()) {
00628
00629
00630
00631
00632 string buf;
00633 needed_rev_num = get_revision_number();
00634 pack_uint(buf, needed_rev_num);
00635 conn.send_message(REPL_REPLY_DB_FOOTER, buf, 0.0);
00636 if (info != NULL && start_rev_num == needed_rev_num)
00637 info->changed = true;
00638 } else {
00639
00640
00641
00642
00643
00644
00645
00646
00647 string buf;
00648 pack_uint(buf, start_rev_num + 1);
00649 conn.send_message(REPL_REPLY_DB_FOOTER, buf, 0.0);
00650 need_whole_db = true;
00651 }
00652 } else {
00653
00654 if (start_rev_num >= get_revision_number()) {
00655 reopen();
00656 if (start_uuid != get_uuid()) {
00657 need_whole_db = true;
00658 continue;
00659 }
00660 if (start_rev_num >= get_revision_number()) {
00661 break;
00662 }
00663 }
00664
00665
00666 string changes_name = db_dir + "/changes" + str(start_rev_num);
00667 #ifdef __WIN32__
00668 int fd_changes = msvc_posix_open(changes_name.c_str(), O_RDONLY);
00669 #else
00670 int fd_changes = open(changes_name.c_str(), O_RDONLY);
00671 #endif
00672 if (fd_changes > 0) {
00673 fdcloser closefd(fd_changes);
00674
00675
00676
00677 brass_revision_number_t changeset_start_rev_num;
00678 brass_revision_number_t changeset_end_rev_num;
00679 get_changeset_revisions(changes_name,
00680 &changeset_start_rev_num,
00681 &changeset_end_rev_num);
00682 if (changeset_start_rev_num != start_rev_num) {
00683 throw Xapian::DatabaseError("Changeset start revision does not match changeset filename");
00684 }
00685 if (changeset_start_rev_num >= changeset_end_rev_num) {
00686 throw Xapian::DatabaseError("Changeset start revision is not less than end revision");
00687 }
00688
00689 conn.send_file(REPL_REPLY_CHANGESET, fd_changes, 0.0);
00690 start_rev_num = changeset_end_rev_num;
00691 if (info != NULL) {
00692 ++(info->changeset_count);
00693 if (start_rev_num >= needed_rev_num)
00694 info->changed = true;
00695 }
00696 } else {
00697
00698
00699 need_whole_db = true;
00700 }
00701 }
00702 }
00703 conn.send_message(REPL_REPLY_END_OF_CHANGES, string(), 0.0);
00704 }
00705
00706 void
00707 BrassDatabase::modifications_failed(brass_revision_number_t old_revision,
00708 brass_revision_number_t new_revision,
00709 const std::string & msg)
00710 {
00711
00712 try {
00713
00714
00715 cancel();
00716
00717
00718 open_tables(old_revision);
00719
00720
00721
00722 ++new_revision;
00723 set_revision_number(new_revision);
00724 } catch (const Xapian::Error &e) {
00725
00726
00727 BrassDatabase::close();
00728 throw Xapian::DatabaseError("Modifications failed (" + msg +
00729 "), and cannot set consistent table "
00730 "revision numbers: " + e.get_msg());
00731 }
00732 }
00733
00734 void
00735 BrassDatabase::apply()
00736 {
00737 LOGCALL_VOID(DB, "BrassDatabase::apply", NO_ARGS);
00738 if (!postlist_table.is_modified() &&
00739 !position_table.is_modified() &&
00740 !termlist_table.is_modified() &&
00741 !value_manager.is_modified() &&
00742 !synonym_table.is_modified() &&
00743 !spelling_table.is_modified() &&
00744 !record_table.is_modified()) {
00745 return;
00746 }
00747
00748 brass_revision_number_t old_revision = get_revision_number();
00749 brass_revision_number_t new_revision = get_next_revision_number();
00750
00751 try {
00752 set_revision_number(new_revision);
00753 } catch (const Xapian::Error &e) {
00754 modifications_failed(old_revision, new_revision, e.get_description());
00755 throw;
00756 } catch (...) {
00757 modifications_failed(old_revision, new_revision, "Unknown error");
00758 throw;
00759 }
00760 }
00761
00762 void
00763 BrassDatabase::cancel()
00764 {
00765 LOGCALL_VOID(DB, "BrassDatabase::cancel", NO_ARGS);
00766 postlist_table.cancel();
00767 position_table.cancel();
00768 termlist_table.cancel();
00769 value_manager.cancel();
00770 synonym_table.cancel();
00771 spelling_table.cancel();
00772 record_table.cancel();
00773 }
00774
00775 Xapian::doccount
00776 BrassDatabase::get_doccount() const
00777 {
00778 LOGCALL(DB, Xapian::doccount, "BrassDatabase::get_doccount", NO_ARGS);
00779 RETURN(record_table.get_doccount());
00780 }
00781
00782 Xapian::docid
00783 BrassDatabase::get_lastdocid() const
00784 {
00785 LOGCALL(DB, Xapian::docid, "BrassDatabase::get_lastdocid", NO_ARGS);
00786 RETURN(stats.get_last_docid());
00787 }
00788
00789 totlen_t
00790 BrassDatabase::get_total_length() const
00791 {
00792 LOGCALL(DB, totlen_t, "BrassDatabase::get_total_length", NO_ARGS);
00793 RETURN(stats.get_total_doclen());
00794 }
00795
00796 Xapian::doclength
00797 BrassDatabase::get_avlength() const
00798 {
00799 LOGCALL(DB, Xapian::doclength, "BrassDatabase::get_avlength", NO_ARGS);
00800 Xapian::doccount doccount = record_table.get_doccount();
00801 if (doccount == 0) {
00802
00803 RETURN(0);
00804 }
00805 RETURN(double(stats.get_total_doclen()) / doccount);
00806 }
00807
00808 Xapian::termcount
00809 BrassDatabase::get_doclength(Xapian::docid did) const
00810 {
00811 LOGCALL(DB, Xapian::termcount, "BrassDatabase::get_doclength", did);
00812 Assert(did != 0);
00813 Xapian::Internal::RefCntPtr<const BrassDatabase> ptrtothis(this);
00814 RETURN(postlist_table.get_doclength(did, ptrtothis));
00815 }
00816
00817 Xapian::doccount
00818 BrassDatabase::get_termfreq(const string & term) const
00819 {
00820 LOGCALL(DB, Xapian::doccount, "BrassDatabase::get_termfreq", term);
00821 Assert(!term.empty());
00822 RETURN(postlist_table.get_termfreq(term));
00823 }
00824
00825 Xapian::termcount
00826 BrassDatabase::get_collection_freq(const string & term) const
00827 {
00828 LOGCALL(DB, Xapian::termcount, "BrassDatabase::get_collection_freq", term);
00829 Assert(!term.empty());
00830 RETURN(postlist_table.get_collection_freq(term));
00831 }
00832
00833 Xapian::doccount
00834 BrassDatabase::get_value_freq(Xapian::valueno slot) const
00835 {
00836 LOGCALL(DB, Xapian::doccount, "BrassDatabase::get_value_freq", slot);
00837 RETURN(value_manager.get_value_freq(slot));
00838 }
00839
00840 std::string
00841 BrassDatabase::get_value_lower_bound(Xapian::valueno slot) const
00842 {
00843 LOGCALL(DB, std::string, "BrassDatabase::get_value_lower_bound", slot);
00844 RETURN(value_manager.get_value_lower_bound(slot));
00845 }
00846
00847 std::string
00848 BrassDatabase::get_value_upper_bound(Xapian::valueno slot) const
00849 {
00850 LOGCALL(DB, std::string, "BrassDatabase::get_value_upper_bound", slot);
00851 RETURN(value_manager.get_value_upper_bound(slot));
00852 }
00853
00854 Xapian::termcount
00855 BrassDatabase::get_doclength_lower_bound() const
00856 {
00857 return stats.get_doclength_lower_bound();
00858 }
00859
00860 Xapian::termcount
00861 BrassDatabase::get_doclength_upper_bound() const
00862 {
00863 return stats.get_doclength_upper_bound();
00864 }
00865
00866 Xapian::termcount
00867 BrassDatabase::get_wdf_upper_bound(const string & term) const
00868 {
00869 return min(get_collection_freq(term), stats.get_wdf_upper_bound());
00870 }
00871
00872 bool
00873 BrassDatabase::term_exists(const string & term) const
00874 {
00875 LOGCALL(DB, bool, "BrassDatabase::term_exists", term);
00876 Assert(!term.empty());
00877 return postlist_table.term_exists(term);
00878 }
00879
00880 bool
00881 BrassDatabase::has_positions() const
00882 {
00883 return !position_table.empty();
00884 }
00885
00886 LeafPostList *
00887 BrassDatabase::open_post_list(const string& term) const
00888 {
00889 LOGCALL(DB, LeafPostList *, "BrassDatabase::open_post_list", term);
00890 Xapian::Internal::RefCntPtr<const BrassDatabase> ptrtothis(this);
00891
00892 if (term.empty()) {
00893 Xapian::doccount doccount = get_doccount();
00894 if (stats.get_last_docid() == doccount) {
00895 RETURN(new ContiguousAllDocsPostList(ptrtothis, doccount));
00896 }
00897 RETURN(new BrassAllDocsPostList(ptrtothis, doccount));
00898 }
00899
00900 RETURN(new BrassPostList(ptrtothis, term, true));
00901 }
00902
00903 ValueList *
00904 BrassDatabase::open_value_list(Xapian::valueno slot) const
00905 {
00906 LOGCALL(DB, ValueList *, "BrassDatabase::open_value_list", slot);
00907 Xapian::Internal::RefCntPtr<const BrassDatabase> ptrtothis(this);
00908 RETURN(new BrassValueList(slot, ptrtothis));
00909 }
00910
00911 TermList *
00912 BrassDatabase::open_term_list(Xapian::docid did) const
00913 {
00914 LOGCALL(DB, TermList *, "BrassDatabase::open_term_list", did);
00915 Assert(did != 0);
00916 if (!termlist_table.is_open())
00917 throw Xapian::FeatureUnavailableError("Database has no termlist");
00918
00919 Xapian::Internal::RefCntPtr<const BrassDatabase> ptrtothis(this);
00920 RETURN(new BrassTermList(ptrtothis, did));
00921 }
00922
00923 Xapian::Document::Internal *
00924 BrassDatabase::open_document(Xapian::docid did, bool lazy) const
00925 {
00926 LOGCALL(DB, Xapian::Document::Internal *, "BrassDatabase::open_document", did | lazy);
00927 Assert(did != 0);
00928 if (!lazy) {
00929
00930 (void)get_doclength(did);
00931 }
00932
00933 Xapian::Internal::RefCntPtr<const Database::Internal> ptrtothis(this);
00934 RETURN(new BrassDocument(ptrtothis, did, &value_manager, &record_table));
00935 }
00936
00937 PositionList *
00938 BrassDatabase::open_position_list(Xapian::docid did, const string & term) const
00939 {
00940 Assert(did != 0);
00941
00942 AutoPtr<BrassPositionList> poslist(new BrassPositionList);
00943 if (!poslist->read_data(&position_table, did, term)) {
00944
00945
00946
00947 }
00948
00949 return poslist.release();
00950 }
00951
00952 TermList *
00953 BrassDatabase::open_allterms(const string & prefix) const
00954 {
00955 LOGCALL(DB, TermList *, "BrassDatabase::open_allterms", NO_ARGS);
00956 RETURN(new BrassAllTermsList(Xapian::Internal::RefCntPtr<const BrassDatabase>(this),
00957 prefix));
00958 }
00959
00960 TermList *
00961 BrassDatabase::open_spelling_termlist(const string & word) const
00962 {
00963 return spelling_table.open_termlist(word);
00964 }
00965
00966 TermList *
00967 BrassDatabase::open_spelling_wordlist() const
00968 {
00969 BrassCursor * cursor = spelling_table.cursor_get();
00970 if (!cursor) return NULL;
00971 return new BrassSpellingWordsList(Xapian::Internal::RefCntPtr<const BrassDatabase>(this),
00972 cursor);
00973 }
00974
00975 Xapian::doccount
00976 BrassDatabase::get_spelling_frequency(const string & word) const
00977 {
00978 return spelling_table.get_word_frequency(word);
00979 }
00980
00981 TermList *
00982 BrassDatabase::open_synonym_termlist(const string & term) const
00983 {
00984 return synonym_table.open_termlist(term);
00985 }
00986
00987 TermList *
00988 BrassDatabase::open_synonym_keylist(const string & prefix) const
00989 {
00990 BrassCursor * cursor = synonym_table.cursor_get();
00991 if (!cursor) return NULL;
00992 return new BrassSynonymTermList(Xapian::Internal::RefCntPtr<const BrassDatabase>(this),
00993 cursor, prefix);
00994 }
00995
00996 string
00997 BrassDatabase::get_metadata(const string & key) const
00998 {
00999 LOGCALL(DB, string, "BrassDatabase::get_metadata", key);
01000 string btree_key("\x00\xc0", 2);
01001 btree_key += key;
01002 string tag;
01003 (void)postlist_table.get_exact_entry(btree_key, tag);
01004 RETURN(tag);
01005 }
01006
01007 TermList *
01008 BrassDatabase::open_metadata_keylist(const std::string &prefix) const
01009 {
01010 LOGCALL(DB, string, "BrassDatabase::open_metadata_keylist", NO_ARGS);
01011 BrassCursor * cursor = postlist_table.cursor_get();
01012 if (!cursor) return NULL;
01013 return new BrassMetadataTermList(Xapian::Internal::RefCntPtr<const BrassDatabase>(this),
01014 cursor, prefix);
01015 }
01016
01017 string
01018 BrassDatabase::get_revision_info() const
01019 {
01020 LOGCALL(DB, string, "BrassDatabase::get_revision_info", NO_ARGS);
01021 string buf;
01022 pack_uint(buf, get_revision_number());
01023 RETURN(buf);
01024 }
01025
01026 string
01027 BrassDatabase::get_uuid() const
01028 {
01029 LOGCALL(DB, string, "BrassDatabase::get_uuid", NO_ARGS);
01030 RETURN(version_file.get_uuid_string());
01031 }
01032
01034
01035 BrassWritableDatabase::BrassWritableDatabase(const string &dir, int action,
01036 int block_size)
01037 : BrassDatabase(dir, action, block_size),
01038 change_count(0),
01039 flush_threshold(0),
01040 modify_shortcut_document(NULL),
01041 modify_shortcut_docid(0)
01042 {
01043 LOGCALL_CTOR(DB, "BrassWritableDatabase", dir | action | block_size);
01044
01045 const char *p = getenv("XAPIAN_FLUSH_THRESHOLD");
01046 if (p)
01047 flush_threshold = atoi(p);
01048 if (flush_threshold == 0)
01049 flush_threshold = 10000;
01050 }
01051
01052 BrassWritableDatabase::~BrassWritableDatabase()
01053 {
01054 LOGCALL_DTOR(DB, "~BrassWritableDatabase");
01055 dtor_called();
01056 }
01057
01058 void
01059 BrassWritableDatabase::commit()
01060 {
01061 if (transaction_active())
01062 throw Xapian::InvalidOperationError("Can't commit during a transaction");
01063 if (change_count) flush_postlist_changes();
01064 apply();
01065 }
01066
01067 void
01068 BrassWritableDatabase::flush_postlist_changes() const
01069 {
01070 stats.write(postlist_table);
01071 inverter.flush(postlist_table);
01072
01073 change_count = 0;
01074 }
01075
01076 void
01077 BrassWritableDatabase::close()
01078 {
01079 LOGCALL_VOID(DB, "BrassWritableDatabase::close", NO_ARGS);
01080 if (!transaction_active()) {
01081 commit();
01082
01083 }
01084 BrassDatabase::close();
01085 }
01086
01087 void
01088 BrassWritableDatabase::apply()
01089 {
01090 value_manager.set_value_stats(value_stats);
01091 BrassDatabase::apply();
01092 }
01093
01094 Xapian::docid
01095 BrassWritableDatabase::add_document(const Xapian::Document & document)
01096 {
01097 LOGCALL(DB, Xapian::docid, "BrassWritableDatabase::add_document", document);
01098
01099 if (stats.get_last_docid() == Xapian::docid(-1))
01100 throw Xapian::DatabaseError("Run out of docids - you'll have to use copydatabase to eliminate any gaps before you can add more documents");
01101
01102 RETURN(add_document_(stats.get_next_docid(), document));
01103 }
01104
01105 Xapian::docid
01106 BrassWritableDatabase::add_document_(Xapian::docid did,
01107 const Xapian::Document & document)
01108 {
01109 LOGCALL(DB, Xapian::docid, "BrassWritableDatabase::add_document_", did | document);
01110 Assert(did != 0);
01111 try {
01112
01113 record_table.replace_record(document.get_data(), did);
01114
01115
01116 value_manager.add_document(did, document, value_stats);
01117
01118 brass_doclen_t new_doclen = 0;
01119 {
01120 Xapian::TermIterator term = document.termlist_begin();
01121 Xapian::TermIterator term_end = document.termlist_end();
01122 for ( ; term != term_end; ++term) {
01123 termcount wdf = term.get_wdf();
01124
01125 new_doclen += wdf;
01126 stats.check_wdf(wdf);
01127
01128 string tname = *term;
01129 if (tname.size() > MAX_SAFE_TERM_LENGTH)
01130 throw Xapian::InvalidArgumentError("Term too long (> "STRINGIZE(MAX_SAFE_TERM_LENGTH)"): " + tname);
01131
01132 inverter.add_posting(did, tname, wdf);
01133
01134 PositionIterator pos = term.positionlist_begin();
01135 if (pos != term.positionlist_end()) {
01136 position_table.set_positionlist(
01137 did, tname,
01138 pos, term.positionlist_end(), false);
01139 }
01140 }
01141 }
01142 LOGLINE(DB, "Calculated doclen for new document " << did << " as " << new_doclen);
01143
01144
01145 if (termlist_table.is_open())
01146 termlist_table.set_termlist(did, document, new_doclen);
01147
01148
01149 inverter.set_doclength(did, new_doclen, true);
01150 stats.add_document(new_doclen);
01151 } catch (...) {
01152
01153
01154
01155
01156 cancel();
01157 throw;
01158 }
01159
01160
01161
01162
01163 if (++change_count >= flush_threshold) {
01164 flush_postlist_changes();
01165 if (!transaction_active()) apply();
01166 }
01167
01168 RETURN(did);
01169 }
01170
01171 void
01172 BrassWritableDatabase::delete_document(Xapian::docid did)
01173 {
01174 LOGCALL_VOID(DB, "BrassWritableDatabase::delete_document", did);
01175 Assert(did != 0);
01176
01177 if (!termlist_table.is_open())
01178 throw Xapian::FeatureUnavailableError("Database has no termlist");
01179
01180 if (rare(modify_shortcut_docid == did)) {
01181
01182
01183 modify_shortcut_document = NULL;
01184 modify_shortcut_docid = 0;
01185 }
01186
01187
01188
01189
01190 record_table.delete_record(did);
01191
01192 try {
01193
01194 value_manager.delete_document(did, value_stats);
01195
01196
01197 Xapian::Internal::RefCntPtr<const BrassWritableDatabase> ptrtothis(this);
01198 BrassTermList termlist(ptrtothis, did);
01199
01200 stats.delete_document(termlist.get_doclength());
01201
01202 termlist.next();
01203 while (!termlist.at_end()) {
01204 string tname = termlist.get_termname();
01205 position_table.delete_positionlist(did, tname);
01206
01207 inverter.remove_posting(did, tname, termlist.get_wdf());
01208
01209 termlist.next();
01210 }
01211
01212
01213 if (termlist_table.is_open())
01214 termlist_table.delete_termlist(did);
01215
01216
01217 inverter.delete_doclength(did);
01218 } catch (...) {
01219
01220
01221
01222
01223 cancel();
01224 throw;
01225 }
01226
01227 if (++change_count >= flush_threshold) {
01228 flush_postlist_changes();
01229 if (!transaction_active()) apply();
01230 }
01231 }
01232
01233 void
01234 BrassWritableDatabase::replace_document(Xapian::docid did,
01235 const Xapian::Document & document)
01236 {
01237 LOGCALL_VOID(DB, "BrassWritableDatabase::replace_document", did | document);
01238 Assert(did != 0);
01239
01240 try {
01241 if (did > stats.get_last_docid()) {
01242 stats.set_last_docid(did);
01243
01244
01245 (void)add_document_(did, document);
01246 return;
01247 }
01248
01249 if (!termlist_table.is_open()) {
01250
01251 Xapian::Internal::RefCntPtr<const BrassDatabase> ptrtothis(this);
01252 if (!postlist_table.document_exists(did, ptrtothis)) {
01253 (void)add_document_(did, document);
01254 return;
01255 }
01256 throw Xapian::FeatureUnavailableError("Database has no termlist");
01257 }
01258
01259
01260
01261 bool modifying = false;
01262 if (modify_shortcut_docid &&
01263 document.internal->get_docid() == modify_shortcut_docid) {
01264 if (document.internal.get() == modify_shortcut_document) {
01265
01266
01267
01268 if (!document.internal->modified()) {
01269
01270 return;
01271 }
01272 modifying = true;
01273 LOGLINE(DB, "Detected potential document modification shortcut.");
01274 } else {
01275
01276
01277
01278 modify_shortcut_document = NULL;
01279 modify_shortcut_docid = 0;
01280 }
01281 }
01282
01283 if (!modifying || document.internal->terms_modified()) {
01284 bool pos_modified = !modifying ||
01285 document.internal->term_positions_modified();
01286 Xapian::Internal::RefCntPtr<const BrassWritableDatabase> ptrtothis(this);
01287 BrassTermList termlist(ptrtothis, did);
01288 Xapian::TermIterator term = document.termlist_begin();
01289 brass_doclen_t old_doclen = termlist.get_doclength();
01290 stats.delete_document(old_doclen);
01291 brass_doclen_t new_doclen = old_doclen;
01292
01293 string old_tname, new_tname;
01294
01295 termlist.next();
01296 while (!termlist.at_end() || term != document.termlist_end()) {
01297 int cmp;
01298 if (termlist.at_end()) {
01299 cmp = 1;
01300 new_tname = *term;
01301 } else {
01302 old_tname = termlist.get_termname();
01303 if (term != document.termlist_end()) {
01304 new_tname = *term;
01305 cmp = old_tname.compare(new_tname);
01306 } else {
01307 cmp = -1;
01308 }
01309 }
01310
01311 if (cmp < 0) {
01312
01313 termcount old_wdf = termlist.get_wdf();
01314 new_doclen -= old_wdf;
01315 inverter.remove_posting(did, old_tname, old_wdf);
01316 if (pos_modified)
01317 position_table.delete_positionlist(did, old_tname);
01318 termlist.next();
01319 } else if (cmp > 0) {
01320
01321 termcount new_wdf = term.get_wdf();
01322 new_doclen += new_wdf;
01323 stats.check_wdf(new_wdf);
01324 if (new_tname.size() > MAX_SAFE_TERM_LENGTH)
01325 throw Xapian::InvalidArgumentError("Term too long (> "STRINGIZE(MAX_SAFE_TERM_LENGTH)"): " + new_tname);
01326 inverter.add_posting(did, new_tname, new_wdf);
01327 if (pos_modified) {
01328 PositionIterator pos = term.positionlist_begin();
01329 if (pos != term.positionlist_end()) {
01330 position_table.set_positionlist(
01331 did, new_tname,
01332 pos, term.positionlist_end(), false);
01333 }
01334 }
01335 ++term;
01336 } else if (cmp == 0) {
01337
01338 termcount old_wdf = termlist.get_wdf();
01339 termcount new_wdf = term.get_wdf();
01340
01341
01342
01343
01344 stats.check_wdf(new_wdf);
01345
01346 if (old_wdf != new_wdf) {
01347 new_doclen += new_wdf - old_wdf;
01348 inverter.update_posting(did, new_tname, old_wdf, new_wdf);
01349 }
01350
01351 if (pos_modified) {
01352 PositionIterator pos = term.positionlist_begin();
01353 if (pos != term.positionlist_end()) {
01354 position_table.set_positionlist(did, new_tname, pos,
01355 term.positionlist_end(),
01356 true);
01357 } else {
01358 position_table.delete_positionlist(did, new_tname);
01359 }
01360 }
01361
01362 ++term;
01363 termlist.next();
01364 }
01365 }
01366 LOGLINE(DB, "Calculated doclen for replacement document " << did << " as " << new_doclen);
01367
01368
01369 if (termlist_table.is_open())
01370 termlist_table.set_termlist(did, document, new_doclen);
01371
01372
01373 if (new_doclen != old_doclen)
01374 inverter.set_doclength(did, new_doclen, false);
01375 stats.add_document(new_doclen);
01376 }
01377
01378 if (!modifying || document.internal->data_modified()) {
01379
01380 record_table.replace_record(document.get_data(), did);
01381 }
01382
01383 if (!modifying || document.internal->values_modified()) {
01384
01385 value_manager.replace_document(did, document, value_stats);
01386 }
01387 } catch (const Xapian::DocNotFoundError &) {
01388 (void)add_document_(did, document);
01389 return;
01390 } catch (...) {
01391
01392
01393
01394
01395 cancel();
01396 throw;
01397 }
01398
01399 if (++change_count >= flush_threshold) {
01400 flush_postlist_changes();
01401 if (!transaction_active()) apply();
01402 }
01403 }
01404
01405 Xapian::Document::Internal *
01406 BrassWritableDatabase::open_document(Xapian::docid did, bool lazy) const
01407 {
01408 LOGCALL(DB, Xapian::Document::Internal *, "BrassWritableDatabase::open_document", did | lazy);
01409 modify_shortcut_document = BrassDatabase::open_document(did, lazy);
01410
01411
01412 modify_shortcut_docid = did;
01413 RETURN(modify_shortcut_document);
01414 }
01415
01416 Xapian::termcount
01417 BrassWritableDatabase::get_doclength(Xapian::docid did) const
01418 {
01419 LOGCALL(DB, Xapian::termcount, "BrassWritableDatabase::get_doclength", did);
01420 Xapian::termcount doclen;
01421 if (inverter.get_doclength(did, doclen))
01422 RETURN(doclen);
01423 RETURN(BrassDatabase::get_doclength(did));
01424 }
01425
01426 Xapian::doccount
01427 BrassWritableDatabase::get_termfreq(const string & term) const
01428 {
01429 LOGCALL(DB, Xapian::doccount, "BrassWritableDatabase::get_termfreq", term);
01430 RETURN(BrassDatabase::get_termfreq(term) + inverter.get_tfdelta(term));
01431 }
01432
01433 Xapian::termcount
01434 BrassWritableDatabase::get_collection_freq(const string & term) const
01435 {
01436 LOGCALL(DB, Xapian::termcount, "BrassWritableDatabase::get_collection_freq", term);
01437 RETURN(BrassDatabase::get_collection_freq(term) + inverter.get_cfdelta(term));
01438 }
01439
01440 Xapian::doccount
01441 BrassWritableDatabase::get_value_freq(Xapian::valueno slot) const
01442 {
01443 LOGCALL(DB, Xapian::doccount, "BrassWritableDatabase::get_value_freq", slot);
01444 map<Xapian::valueno, ValueStats>::const_iterator i;
01445 i = value_stats.find(slot);
01446 if (i != value_stats.end()) RETURN(i->second.freq);
01447 RETURN(BrassDatabase::get_value_freq(slot));
01448 }
01449
01450 std::string
01451 BrassWritableDatabase::get_value_lower_bound(Xapian::valueno slot) const
01452 {
01453 LOGCALL(DB, std::string, "BrassWritableDatabase::get_value_lower_bound", slot);
01454 map<Xapian::valueno, ValueStats>::const_iterator i;
01455 i = value_stats.find(slot);
01456 if (i != value_stats.end()) RETURN(i->second.lower_bound);
01457 RETURN(BrassDatabase::get_value_lower_bound(slot));
01458 }
01459
01460 std::string
01461 BrassWritableDatabase::get_value_upper_bound(Xapian::valueno slot) const
01462 {
01463 LOGCALL(DB, std::string, "BrassWritableDatabase::get_value_upper_bound", slot);
01464 map<Xapian::valueno, ValueStats>::const_iterator i;
01465 i = value_stats.find(slot);
01466 if (i != value_stats.end()) RETURN(i->second.upper_bound);
01467 RETURN(BrassDatabase::get_value_upper_bound(slot));
01468 }
01469
01470 bool
01471 BrassWritableDatabase::term_exists(const string & tname) const
01472 {
01473 LOGCALL(DB, bool, "BrassWritableDatabase::term_exists", tname);
01474 RETURN(get_termfreq(tname) != 0);
01475 }
01476
01477 LeafPostList *
01478 BrassWritableDatabase::open_post_list(const string& tname) const
01479 {
01480 LOGCALL(DB, LeafPostList *, "BrassWritableDatabase::open_post_list", tname);
01481 Xapian::Internal::RefCntPtr<const BrassWritableDatabase> ptrtothis(this);
01482
01483 if (tname.empty()) {
01484 Xapian::doccount doccount = get_doccount();
01485 if (stats.get_last_docid() == doccount) {
01486 RETURN(new ContiguousAllDocsPostList(ptrtothis, doccount));
01487 }
01488 inverter.flush_doclengths(postlist_table);
01489 RETURN(new BrassAllDocsPostList(ptrtothis, doccount));
01490 }
01491
01492
01493
01494 inverter.flush_post_list(postlist_table, tname);
01495 RETURN(new BrassPostList(ptrtothis, tname, true));
01496 }
01497
01498 ValueList *
01499 BrassWritableDatabase::open_value_list(Xapian::valueno slot) const
01500 {
01501 LOGCALL(DB, ValueList *, "BrassWritableDatabase::open_value_list", slot);
01502
01503
01504
01505 if (change_count) value_manager.merge_changes();
01506 RETURN(BrassDatabase::open_value_list(slot));
01507 }
01508
01509 TermList *
01510 BrassWritableDatabase::open_allterms(const string & prefix) const
01511 {
01512 LOGCALL(DB, TermList *, "BrassWritableDatabase::open_allterms", NO_ARGS);
01513 if (change_count) {
01514
01515
01516
01517 inverter.flush_post_lists(postlist_table, prefix);
01518 if (prefix.empty()) {
01519
01520
01521
01522 change_count = 1;
01523 }
01524 }
01525 RETURN(BrassDatabase::open_allterms(prefix));
01526 }
01527
01528 void
01529 BrassWritableDatabase::cancel()
01530 {
01531 BrassDatabase::cancel();
01532 stats.read(postlist_table);
01533
01534 inverter.clear();
01535 value_stats.clear();
01536 change_count = 0;
01537 }
01538
01539 void
01540 BrassWritableDatabase::add_spelling(const string & word,
01541 Xapian::termcount freqinc) const
01542 {
01543 spelling_table.add_word(word, freqinc);
01544 }
01545
01546 void
01547 BrassWritableDatabase::remove_spelling(const string & word,
01548 Xapian::termcount freqdec) const
01549 {
01550 spelling_table.remove_word(word, freqdec);
01551 }
01552
01553 TermList *
01554 BrassWritableDatabase::open_spelling_wordlist() const
01555 {
01556 spelling_table.merge_changes();
01557 return BrassDatabase::open_spelling_wordlist();
01558 }
01559
01560 TermList *
01561 BrassWritableDatabase::open_synonym_keylist(const string & prefix) const
01562 {
01563 synonym_table.merge_changes();
01564 return BrassDatabase::open_synonym_keylist(prefix);
01565 }
01566
01567 void
01568 BrassWritableDatabase::add_synonym(const string & term,
01569 const string & synonym) const
01570 {
01571 synonym_table.add_synonym(term, synonym);
01572 }
01573
01574 void
01575 BrassWritableDatabase::remove_synonym(const string & term,
01576 const string & synonym) const
01577 {
01578 synonym_table.remove_synonym(term, synonym);
01579 }
01580
01581 void
01582 BrassWritableDatabase::clear_synonyms(const string & term) const
01583 {
01584 synonym_table.clear_synonyms(term);
01585 }
01586
01587 void
01588 BrassWritableDatabase::set_metadata(const string & key, const string & value)
01589 {
01590 LOGCALL(DB, string, "BrassWritableDatabase::set_metadata", key | value);
01591 string btree_key("\x00\xc0", 2);
01592 btree_key += key;
01593 if (value.empty()) {
01594 postlist_table.del(btree_key);
01595 } else {
01596 postlist_table.add(btree_key, value);
01597 }
01598 }
01599
01600 void
01601 BrassWritableDatabase::invalidate_doc_object(Xapian::Document::Internal * obj) const
01602 {
01603 if (obj == modify_shortcut_document) {
01604 modify_shortcut_document = NULL;
01605 modify_shortcut_docid = 0;
01606 }
01607 }