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 | O_BINARY);
00340 #else
00341 changes_fd = open(path.c_str(), O_RDONLY | O_BINARY);
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 | O_BINARY);
00564 #else
00565 int fd = open(filepath.c_str(), O_RDONLY | O_BINARY);
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 | O_BINARY);
00669 #else
00670 int fd_changes = open(changes_name.c_str(), O_RDONLY | O_BINARY);
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_termlist_table_close_exception();
00918 Xapian::Internal::RefCntPtr<const BrassDatabase> ptrtothis(this);
00919 RETURN(new BrassTermList(ptrtothis, did));
00920 }
00921
00922 Xapian::Document::Internal *
00923 BrassDatabase::open_document(Xapian::docid did, bool lazy) const
00924 {
00925 LOGCALL(DB, Xapian::Document::Internal *, "BrassDatabase::open_document", did | lazy);
00926 Assert(did != 0);
00927 if (!lazy) {
00928
00929 (void)get_doclength(did);
00930 }
00931
00932 Xapian::Internal::RefCntPtr<const Database::Internal> ptrtothis(this);
00933 RETURN(new BrassDocument(ptrtothis, did, &value_manager, &record_table));
00934 }
00935
00936 PositionList *
00937 BrassDatabase::open_position_list(Xapian::docid did, const string & term) const
00938 {
00939 Assert(did != 0);
00940
00941 AutoPtr<BrassPositionList> poslist(new BrassPositionList);
00942 if (!poslist->read_data(&position_table, did, term)) {
00943
00944
00945
00946 }
00947
00948 return poslist.release();
00949 }
00950
00951 TermList *
00952 BrassDatabase::open_allterms(const string & prefix) const
00953 {
00954 LOGCALL(DB, TermList *, "BrassDatabase::open_allterms", NO_ARGS);
00955 RETURN(new BrassAllTermsList(Xapian::Internal::RefCntPtr<const BrassDatabase>(this),
00956 prefix));
00957 }
00958
00959 TermList *
00960 BrassDatabase::open_spelling_termlist(const string & word) const
00961 {
00962 return spelling_table.open_termlist(word);
00963 }
00964
00965 TermList *
00966 BrassDatabase::open_spelling_wordlist() const
00967 {
00968 BrassCursor * cursor = spelling_table.cursor_get();
00969 if (!cursor) return NULL;
00970 return new BrassSpellingWordsList(Xapian::Internal::RefCntPtr<const BrassDatabase>(this),
00971 cursor);
00972 }
00973
00974 Xapian::doccount
00975 BrassDatabase::get_spelling_frequency(const string & word) const
00976 {
00977 return spelling_table.get_word_frequency(word);
00978 }
00979
00980 TermList *
00981 BrassDatabase::open_synonym_termlist(const string & term) const
00982 {
00983 return synonym_table.open_termlist(term);
00984 }
00985
00986 TermList *
00987 BrassDatabase::open_synonym_keylist(const string & prefix) const
00988 {
00989 BrassCursor * cursor = synonym_table.cursor_get();
00990 if (!cursor) return NULL;
00991 return new BrassSynonymTermList(Xapian::Internal::RefCntPtr<const BrassDatabase>(this),
00992 cursor, prefix);
00993 }
00994
00995 string
00996 BrassDatabase::get_metadata(const string & key) const
00997 {
00998 LOGCALL(DB, string, "BrassDatabase::get_metadata", key);
00999 string btree_key("\x00\xc0", 2);
01000 btree_key += key;
01001 string tag;
01002 (void)postlist_table.get_exact_entry(btree_key, tag);
01003 RETURN(tag);
01004 }
01005
01006 TermList *
01007 BrassDatabase::open_metadata_keylist(const std::string &prefix) const
01008 {
01009 LOGCALL(DB, string, "BrassDatabase::open_metadata_keylist", NO_ARGS);
01010 BrassCursor * cursor = postlist_table.cursor_get();
01011 if (!cursor) return NULL;
01012 return new BrassMetadataTermList(Xapian::Internal::RefCntPtr<const BrassDatabase>(this),
01013 cursor, prefix);
01014 }
01015
01016 string
01017 BrassDatabase::get_revision_info() const
01018 {
01019 LOGCALL(DB, string, "BrassDatabase::get_revision_info", NO_ARGS);
01020 string buf;
01021 pack_uint(buf, get_revision_number());
01022 RETURN(buf);
01023 }
01024
01025 string
01026 BrassDatabase::get_uuid() const
01027 {
01028 LOGCALL(DB, string, "BrassDatabase::get_uuid", NO_ARGS);
01029 RETURN(version_file.get_uuid_string());
01030 }
01031
01032 void
01033 BrassDatabase::throw_termlist_table_close_exception() const
01034 {
01035
01036
01037 if (!postlist_table.is_open())
01038 BrassTable::throw_database_closed();
01039 throw Xapian::FeatureUnavailableError("Database has no termlist");
01040 }
01041
01043
01044 BrassWritableDatabase::BrassWritableDatabase(const string &dir, int action,
01045 int block_size)
01046 : BrassDatabase(dir, action, block_size),
01047 change_count(0),
01048 flush_threshold(0),
01049 modify_shortcut_document(NULL),
01050 modify_shortcut_docid(0)
01051 {
01052 LOGCALL_CTOR(DB, "BrassWritableDatabase", dir | action | block_size);
01053
01054 const char *p = getenv("XAPIAN_FLUSH_THRESHOLD");
01055 if (p)
01056 flush_threshold = atoi(p);
01057 if (flush_threshold == 0)
01058 flush_threshold = 10000;
01059 }
01060
01061 BrassWritableDatabase::~BrassWritableDatabase()
01062 {
01063 LOGCALL_DTOR(DB, "~BrassWritableDatabase");
01064 dtor_called();
01065 }
01066
01067 void
01068 BrassWritableDatabase::commit()
01069 {
01070 if (transaction_active())
01071 throw Xapian::InvalidOperationError("Can't commit during a transaction");
01072 if (change_count) flush_postlist_changes();
01073 apply();
01074 }
01075
01076 void
01077 BrassWritableDatabase::flush_postlist_changes() const
01078 {
01079 stats.write(postlist_table);
01080 inverter.flush(postlist_table);
01081
01082 change_count = 0;
01083 }
01084
01085 void
01086 BrassWritableDatabase::close()
01087 {
01088 LOGCALL_VOID(DB, "BrassWritableDatabase::close", NO_ARGS);
01089 if (!transaction_active()) {
01090 commit();
01091
01092 }
01093 BrassDatabase::close();
01094 }
01095
01096 void
01097 BrassWritableDatabase::apply()
01098 {
01099 value_manager.set_value_stats(value_stats);
01100 BrassDatabase::apply();
01101 }
01102
01103 Xapian::docid
01104 BrassWritableDatabase::add_document(const Xapian::Document & document)
01105 {
01106 LOGCALL(DB, Xapian::docid, "BrassWritableDatabase::add_document", document);
01107
01108 if (stats.get_last_docid() == Xapian::docid(-1))
01109 throw Xapian::DatabaseError("Run out of docids - you'll have to use copydatabase to eliminate any gaps before you can add more documents");
01110
01111 RETURN(add_document_(stats.get_next_docid(), document));
01112 }
01113
01114 Xapian::docid
01115 BrassWritableDatabase::add_document_(Xapian::docid did,
01116 const Xapian::Document & document)
01117 {
01118 LOGCALL(DB, Xapian::docid, "BrassWritableDatabase::add_document_", did | document);
01119 Assert(did != 0);
01120 try {
01121
01122 record_table.replace_record(document.get_data(), did);
01123
01124
01125 value_manager.add_document(did, document, value_stats);
01126
01127 brass_doclen_t new_doclen = 0;
01128 {
01129 Xapian::TermIterator term = document.termlist_begin();
01130 Xapian::TermIterator term_end = document.termlist_end();
01131 for ( ; term != term_end; ++term) {
01132 termcount wdf = term.get_wdf();
01133
01134 new_doclen += wdf;
01135 stats.check_wdf(wdf);
01136
01137 string tname = *term;
01138 if (tname.size() > MAX_SAFE_TERM_LENGTH)
01139 throw Xapian::InvalidArgumentError("Term too long (> "STRINGIZE(MAX_SAFE_TERM_LENGTH)"): " + tname);
01140
01141 inverter.add_posting(did, tname, wdf);
01142
01143 PositionIterator pos = term.positionlist_begin();
01144 if (pos != term.positionlist_end()) {
01145 position_table.set_positionlist(
01146 did, tname,
01147 pos, term.positionlist_end(), false);
01148 }
01149 }
01150 }
01151 LOGLINE(DB, "Calculated doclen for new document " << did << " as " << new_doclen);
01152
01153
01154 if (termlist_table.is_open())
01155 termlist_table.set_termlist(did, document, new_doclen);
01156
01157
01158 inverter.set_doclength(did, new_doclen, true);
01159 stats.add_document(new_doclen);
01160 } catch (...) {
01161
01162
01163
01164
01165 cancel();
01166 throw;
01167 }
01168
01169
01170
01171
01172 if (++change_count >= flush_threshold) {
01173 flush_postlist_changes();
01174 if (!transaction_active()) apply();
01175 }
01176
01177 RETURN(did);
01178 }
01179
01180 void
01181 BrassWritableDatabase::delete_document(Xapian::docid did)
01182 {
01183 LOGCALL_VOID(DB, "BrassWritableDatabase::delete_document", did);
01184 Assert(did != 0);
01185
01186 if (!termlist_table.is_open())
01187 throw_termlist_table_close_exception();
01188
01189 if (rare(modify_shortcut_docid == did)) {
01190
01191
01192 modify_shortcut_document = NULL;
01193 modify_shortcut_docid = 0;
01194 }
01195
01196
01197
01198
01199 record_table.delete_record(did);
01200
01201 try {
01202
01203 value_manager.delete_document(did, value_stats);
01204
01205
01206 Xapian::Internal::RefCntPtr<const BrassWritableDatabase> ptrtothis(this);
01207 BrassTermList termlist(ptrtothis, did);
01208
01209 stats.delete_document(termlist.get_doclength());
01210
01211 termlist.next();
01212 while (!termlist.at_end()) {
01213 string tname = termlist.get_termname();
01214 position_table.delete_positionlist(did, tname);
01215
01216 inverter.remove_posting(did, tname, termlist.get_wdf());
01217
01218 termlist.next();
01219 }
01220
01221
01222 if (termlist_table.is_open())
01223 termlist_table.delete_termlist(did);
01224
01225
01226 inverter.delete_doclength(did);
01227 } catch (...) {
01228
01229
01230
01231
01232 cancel();
01233 throw;
01234 }
01235
01236 if (++change_count >= flush_threshold) {
01237 flush_postlist_changes();
01238 if (!transaction_active()) apply();
01239 }
01240 }
01241
01242 void
01243 BrassWritableDatabase::replace_document(Xapian::docid did,
01244 const Xapian::Document & document)
01245 {
01246 LOGCALL_VOID(DB, "BrassWritableDatabase::replace_document", did | document);
01247 Assert(did != 0);
01248
01249 try {
01250 if (did > stats.get_last_docid()) {
01251 stats.set_last_docid(did);
01252
01253
01254 (void)add_document_(did, document);
01255 return;
01256 }
01257
01258 if (!termlist_table.is_open()) {
01259
01260 Xapian::Internal::RefCntPtr<const BrassDatabase> ptrtothis(this);
01261 if (!postlist_table.document_exists(did, ptrtothis)) {
01262 (void)add_document_(did, document);
01263 return;
01264 }
01265 throw_termlist_table_close_exception();
01266 }
01267
01268
01269
01270 bool modifying = false;
01271 if (modify_shortcut_docid &&
01272 document.internal->get_docid() == modify_shortcut_docid) {
01273 if (document.internal.get() == modify_shortcut_document) {
01274
01275
01276
01277 if (!document.internal->modified()) {
01278
01279 return;
01280 }
01281 modifying = true;
01282 LOGLINE(DB, "Detected potential document modification shortcut.");
01283 } else {
01284
01285
01286
01287 modify_shortcut_document = NULL;
01288 modify_shortcut_docid = 0;
01289 }
01290 }
01291
01292 if (!modifying || document.internal->terms_modified()) {
01293 bool pos_modified = !modifying ||
01294 document.internal->term_positions_modified();
01295 Xapian::Internal::RefCntPtr<const BrassWritableDatabase> ptrtothis(this);
01296 BrassTermList termlist(ptrtothis, did);
01297 Xapian::TermIterator term = document.termlist_begin();
01298 brass_doclen_t old_doclen = termlist.get_doclength();
01299 stats.delete_document(old_doclen);
01300 brass_doclen_t new_doclen = old_doclen;
01301
01302 string old_tname, new_tname;
01303
01304 termlist.next();
01305 while (!termlist.at_end() || term != document.termlist_end()) {
01306 int cmp;
01307 if (termlist.at_end()) {
01308 cmp = 1;
01309 new_tname = *term;
01310 } else {
01311 old_tname = termlist.get_termname();
01312 if (term != document.termlist_end()) {
01313 new_tname = *term;
01314 cmp = old_tname.compare(new_tname);
01315 } else {
01316 cmp = -1;
01317 }
01318 }
01319
01320 if (cmp < 0) {
01321
01322 termcount old_wdf = termlist.get_wdf();
01323 new_doclen -= old_wdf;
01324 inverter.remove_posting(did, old_tname, old_wdf);
01325 if (pos_modified)
01326 position_table.delete_positionlist(did, old_tname);
01327 termlist.next();
01328 } else if (cmp > 0) {
01329
01330 termcount new_wdf = term.get_wdf();
01331 new_doclen += new_wdf;
01332 stats.check_wdf(new_wdf);
01333 if (new_tname.size() > MAX_SAFE_TERM_LENGTH)
01334 throw Xapian::InvalidArgumentError("Term too long (> "STRINGIZE(MAX_SAFE_TERM_LENGTH)"): " + new_tname);
01335 inverter.add_posting(did, new_tname, new_wdf);
01336 if (pos_modified) {
01337 PositionIterator pos = term.positionlist_begin();
01338 if (pos != term.positionlist_end()) {
01339 position_table.set_positionlist(
01340 did, new_tname,
01341 pos, term.positionlist_end(), false);
01342 }
01343 }
01344 ++term;
01345 } else if (cmp == 0) {
01346
01347 termcount old_wdf = termlist.get_wdf();
01348 termcount new_wdf = term.get_wdf();
01349
01350
01351
01352
01353 stats.check_wdf(new_wdf);
01354
01355 if (old_wdf != new_wdf) {
01356 new_doclen += new_wdf - old_wdf;
01357 inverter.update_posting(did, new_tname, old_wdf, new_wdf);
01358 }
01359
01360 if (pos_modified) {
01361 PositionIterator pos = term.positionlist_begin();
01362 if (pos != term.positionlist_end()) {
01363 position_table.set_positionlist(did, new_tname, pos,
01364 term.positionlist_end(),
01365 true);
01366 } else {
01367 position_table.delete_positionlist(did, new_tname);
01368 }
01369 }
01370
01371 ++term;
01372 termlist.next();
01373 }
01374 }
01375 LOGLINE(DB, "Calculated doclen for replacement document " << did << " as " << new_doclen);
01376
01377
01378 if (termlist_table.is_open())
01379 termlist_table.set_termlist(did, document, new_doclen);
01380
01381
01382 if (new_doclen != old_doclen)
01383 inverter.set_doclength(did, new_doclen, false);
01384 stats.add_document(new_doclen);
01385 }
01386
01387 if (!modifying || document.internal->data_modified()) {
01388
01389 record_table.replace_record(document.get_data(), did);
01390 }
01391
01392 if (!modifying || document.internal->values_modified()) {
01393
01394 value_manager.replace_document(did, document, value_stats);
01395 }
01396 } catch (const Xapian::DocNotFoundError &) {
01397 (void)add_document_(did, document);
01398 return;
01399 } catch (...) {
01400
01401
01402
01403
01404 cancel();
01405 throw;
01406 }
01407
01408 if (++change_count >= flush_threshold) {
01409 flush_postlist_changes();
01410 if (!transaction_active()) apply();
01411 }
01412 }
01413
01414 Xapian::Document::Internal *
01415 BrassWritableDatabase::open_document(Xapian::docid did, bool lazy) const
01416 {
01417 LOGCALL(DB, Xapian::Document::Internal *, "BrassWritableDatabase::open_document", did | lazy);
01418 modify_shortcut_document = BrassDatabase::open_document(did, lazy);
01419
01420
01421 modify_shortcut_docid = did;
01422 RETURN(modify_shortcut_document);
01423 }
01424
01425 Xapian::termcount
01426 BrassWritableDatabase::get_doclength(Xapian::docid did) const
01427 {
01428 LOGCALL(DB, Xapian::termcount, "BrassWritableDatabase::get_doclength", did);
01429 Xapian::termcount doclen;
01430 if (inverter.get_doclength(did, doclen))
01431 RETURN(doclen);
01432 RETURN(BrassDatabase::get_doclength(did));
01433 }
01434
01435 Xapian::doccount
01436 BrassWritableDatabase::get_termfreq(const string & term) const
01437 {
01438 LOGCALL(DB, Xapian::doccount, "BrassWritableDatabase::get_termfreq", term);
01439 RETURN(BrassDatabase::get_termfreq(term) + inverter.get_tfdelta(term));
01440 }
01441
01442 Xapian::termcount
01443 BrassWritableDatabase::get_collection_freq(const string & term) const
01444 {
01445 LOGCALL(DB, Xapian::termcount, "BrassWritableDatabase::get_collection_freq", term);
01446 RETURN(BrassDatabase::get_collection_freq(term) + inverter.get_cfdelta(term));
01447 }
01448
01449 Xapian::doccount
01450 BrassWritableDatabase::get_value_freq(Xapian::valueno slot) const
01451 {
01452 LOGCALL(DB, Xapian::doccount, "BrassWritableDatabase::get_value_freq", slot);
01453 map<Xapian::valueno, ValueStats>::const_iterator i;
01454 i = value_stats.find(slot);
01455 if (i != value_stats.end()) RETURN(i->second.freq);
01456 RETURN(BrassDatabase::get_value_freq(slot));
01457 }
01458
01459 std::string
01460 BrassWritableDatabase::get_value_lower_bound(Xapian::valueno slot) const
01461 {
01462 LOGCALL(DB, std::string, "BrassWritableDatabase::get_value_lower_bound", slot);
01463 map<Xapian::valueno, ValueStats>::const_iterator i;
01464 i = value_stats.find(slot);
01465 if (i != value_stats.end()) RETURN(i->second.lower_bound);
01466 RETURN(BrassDatabase::get_value_lower_bound(slot));
01467 }
01468
01469 std::string
01470 BrassWritableDatabase::get_value_upper_bound(Xapian::valueno slot) const
01471 {
01472 LOGCALL(DB, std::string, "BrassWritableDatabase::get_value_upper_bound", slot);
01473 map<Xapian::valueno, ValueStats>::const_iterator i;
01474 i = value_stats.find(slot);
01475 if (i != value_stats.end()) RETURN(i->second.upper_bound);
01476 RETURN(BrassDatabase::get_value_upper_bound(slot));
01477 }
01478
01479 bool
01480 BrassWritableDatabase::term_exists(const string & tname) const
01481 {
01482 LOGCALL(DB, bool, "BrassWritableDatabase::term_exists", tname);
01483 RETURN(get_termfreq(tname) != 0);
01484 }
01485
01486 LeafPostList *
01487 BrassWritableDatabase::open_post_list(const string& tname) const
01488 {
01489 LOGCALL(DB, LeafPostList *, "BrassWritableDatabase::open_post_list", tname);
01490 Xapian::Internal::RefCntPtr<const BrassWritableDatabase> ptrtothis(this);
01491
01492 if (tname.empty()) {
01493 Xapian::doccount doccount = get_doccount();
01494 if (stats.get_last_docid() == doccount) {
01495 RETURN(new ContiguousAllDocsPostList(ptrtothis, doccount));
01496 }
01497 inverter.flush_doclengths(postlist_table);
01498 RETURN(new BrassAllDocsPostList(ptrtothis, doccount));
01499 }
01500
01501
01502
01503 inverter.flush_post_list(postlist_table, tname);
01504 RETURN(new BrassPostList(ptrtothis, tname, true));
01505 }
01506
01507 ValueList *
01508 BrassWritableDatabase::open_value_list(Xapian::valueno slot) const
01509 {
01510 LOGCALL(DB, ValueList *, "BrassWritableDatabase::open_value_list", slot);
01511
01512
01513
01514 if (change_count) value_manager.merge_changes();
01515 RETURN(BrassDatabase::open_value_list(slot));
01516 }
01517
01518 TermList *
01519 BrassWritableDatabase::open_allterms(const string & prefix) const
01520 {
01521 LOGCALL(DB, TermList *, "BrassWritableDatabase::open_allterms", NO_ARGS);
01522 if (change_count) {
01523
01524
01525
01526 inverter.flush_post_lists(postlist_table, prefix);
01527 if (prefix.empty()) {
01528
01529
01530
01531 change_count = 1;
01532 }
01533 }
01534 RETURN(BrassDatabase::open_allterms(prefix));
01535 }
01536
01537 void
01538 BrassWritableDatabase::cancel()
01539 {
01540 BrassDatabase::cancel();
01541 stats.read(postlist_table);
01542
01543 inverter.clear();
01544 value_stats.clear();
01545 change_count = 0;
01546 }
01547
01548 void
01549 BrassWritableDatabase::add_spelling(const string & word,
01550 Xapian::termcount freqinc) const
01551 {
01552 spelling_table.add_word(word, freqinc);
01553 }
01554
01555 void
01556 BrassWritableDatabase::remove_spelling(const string & word,
01557 Xapian::termcount freqdec) const
01558 {
01559 spelling_table.remove_word(word, freqdec);
01560 }
01561
01562 TermList *
01563 BrassWritableDatabase::open_spelling_wordlist() const
01564 {
01565 spelling_table.merge_changes();
01566 return BrassDatabase::open_spelling_wordlist();
01567 }
01568
01569 TermList *
01570 BrassWritableDatabase::open_synonym_keylist(const string & prefix) const
01571 {
01572 synonym_table.merge_changes();
01573 return BrassDatabase::open_synonym_keylist(prefix);
01574 }
01575
01576 void
01577 BrassWritableDatabase::add_synonym(const string & term,
01578 const string & synonym) const
01579 {
01580 synonym_table.add_synonym(term, synonym);
01581 }
01582
01583 void
01584 BrassWritableDatabase::remove_synonym(const string & term,
01585 const string & synonym) const
01586 {
01587 synonym_table.remove_synonym(term, synonym);
01588 }
01589
01590 void
01591 BrassWritableDatabase::clear_synonyms(const string & term) const
01592 {
01593 synonym_table.clear_synonyms(term);
01594 }
01595
01596 void
01597 BrassWritableDatabase::set_metadata(const string & key, const string & value)
01598 {
01599 LOGCALL(DB, string, "BrassWritableDatabase::set_metadata", key | value);
01600 string btree_key("\x00\xc0", 2);
01601 btree_key += key;
01602 if (value.empty()) {
01603 postlist_table.del(btree_key);
01604 } else {
01605 postlist_table.add(btree_key, value);
01606 }
01607 }
01608
01609 void
01610 BrassWritableDatabase::invalidate_doc_object(Xapian::Document::Internal * obj) const
01611 {
01612 if (obj == modify_shortcut_document) {
01613 modify_shortcut_document = NULL;
01614 modify_shortcut_docid = 0;
01615 }
01616 }