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