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