using namespace std;
-#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
-
typedef struct {
const char *name;
const char *prefix;
#define DB_ACTION Xapian::DB_CREATE_OR_OPEN
#endif
+#define LOG_XAPIAN_EXCEPTION(message, error) _log_xapian_exception (__location__, message, error)
+
+static void
+_log_xapian_exception (const char *where, notmuch_database_t *notmuch, const Xapian::Error error) {
+ _notmuch_database_log (notmuch,
+ "A Xapian exception occurred at %s: %s\n",
+ where,
+ error.get_msg ().c_str ());
+ notmuch->exception_reported = true;
+}
+
+notmuch_database_mode_t
+_notmuch_database_mode (notmuch_database_t *notmuch)
+{
+ if (notmuch->writable_xapian_db)
+ return NOTMUCH_DATABASE_MODE_READ_WRITE;
+ else
+ return NOTMUCH_DATABASE_MODE_READ_ONLY;
+}
+
/* Here's the current schema for our database (for NOTMUCH_DATABASE_VERSION):
*
* We currently have three different types of documents (mail, ghost,
*/
{ "folder", "XFOLDER:", NOTMUCH_FIELD_EXTERNAL |
NOTMUCH_FIELD_PROCESSOR },
-#if HAVE_XAPIAN_FIELD_PROCESSOR
{ "date", NULL, NOTMUCH_FIELD_EXTERNAL |
NOTMUCH_FIELD_PROCESSOR },
{ "query", NULL, NOTMUCH_FIELD_EXTERNAL |
NOTMUCH_FIELD_PROCESSOR },
-#endif
{ "from", "XFROM", NOTMUCH_FIELD_EXTERNAL |
NOTMUCH_FIELD_PROBABILISTIC |
NOTMUCH_FIELD_PROCESSOR },
return NOTMUCH_STATUS_SUCCESS;
}
-#if HAVE_XAPIAN_FIELD_PROCESSOR
static void
_setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
{
Xapian::FieldProcessor *fp;
if (STRNCMP_LITERAL (prefix->name, "date") == 0)
- fp = (new DateFieldProcessor ())->release ();
- else if (STRNCMP_LITERAL (prefix->name, "query") == 0)
+ fp = (new DateFieldProcessor(NOTMUCH_VALUE_TIMESTAMP))->release ();
+ else if (STRNCMP_LITERAL(prefix->name, "query") == 0)
fp = (new QueryFieldProcessor (*notmuch->query_parser, notmuch))->release ();
else if (STRNCMP_LITERAL (prefix->name, "thread") == 0)
fp = (new ThreadFieldProcessor (*notmuch->query_parser, notmuch))->release ();
_setup_query_field_default (prefix, notmuch);
}
}
-#else
-static inline void
-_setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
-{
- _setup_query_field_default (prefix, notmuch);
-}
-#endif
const char *
_find_prefix (const char *name)
return NULL;
}
-static const struct {
- /* NOTMUCH_FEATURE_* value. */
- _notmuch_features value;
- /* Feature name as it appears in the database. This name should
- * be appropriate for displaying to the user if an older version
- * of notmuch doesn't support this feature. */
- const char *name;
- /* Compatibility flags when this feature is declared. */
- const char *flags;
-} feature_names[] = {
- { NOTMUCH_FEATURE_FILE_TERMS,
- "multiple paths per message", "rw" },
- { NOTMUCH_FEATURE_DIRECTORY_DOCS,
- "relative directory paths", "rw" },
- /* Header values are not required for reading a database because a
- * reader can just refer to the message file. */
- { NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES,
- "from/subject/message-ID in database", "w" },
- { NOTMUCH_FEATURE_BOOL_FOLDER,
- "exact folder:/path: search", "rw" },
- { NOTMUCH_FEATURE_GHOSTS,
- "mail documents for missing messages", "w" },
- /* Knowledge of the index mime-types are not required for reading
- * a database because a reader will just be unable to query
- * them. */
- { NOTMUCH_FEATURE_INDEXED_MIMETYPES,
- "indexed MIME types", "w" },
- { NOTMUCH_FEATURE_LAST_MOD,
- "modification tracking", "w" },
- /* Existing databases will work fine for all queries not involving
- * 'body:' */
- { NOTMUCH_FEATURE_UNPREFIX_BODY_ONLY,
- "index body and headers separately", "w" },
-};
-
const char *
notmuch_status_to_string (notmuch_status_t status)
{
notmuch_status_t
_notmuch_database_ensure_writable (notmuch_database_t *notmuch)
{
- if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY) {
+ if (_notmuch_database_mode (notmuch) == NOTMUCH_DATABASE_MODE_READ_ONLY) {
_notmuch_database_log (notmuch, "Cannot write to a read-only database.\n");
return NOTMUCH_STATUS_READ_ONLY_DATABASE;
}
return new_revision;
}
-/* Parse a database features string from the given database version.
- * Returns the feature bit set.
- *
- * For version < 3, this ignores the features string and returns a
- * hard-coded set of features.
- *
- * If there are unrecognized features that are required to open the
- * database in mode (which should be 'r' or 'w'), return a
- * comma-separated list of unrecognized but required features in
- * *incompat_out suitable for presenting to the user. *incompat_out
- * will be allocated from ctx.
- */
-static _notmuch_features
-_parse_features (const void *ctx, const char *features, unsigned int version,
- char mode, char **incompat_out)
-{
- _notmuch_features res = static_cast<_notmuch_features>(0);
- unsigned int namelen, i;
- size_t llen = 0;
- const char *flags;
-
- /* Prior to database version 3, features were implied by the
- * version number. */
- if (version == 0)
- return NOTMUCH_FEATURES_V0;
- else if (version == 1)
- return NOTMUCH_FEATURES_V1;
- else if (version == 2)
- return NOTMUCH_FEATURES_V2;
-
- /* Parse the features string */
- while ((features = strtok_len_c (features + llen, "\n", &llen)) != NULL) {
- flags = strchr (features, '\t');
- if (! flags || flags > features + llen)
- continue;
- namelen = flags - features;
-
- for (i = 0; i < ARRAY_SIZE (feature_names); ++i) {
- if (strlen (feature_names[i].name) == namelen &&
- strncmp (feature_names[i].name, features, namelen) == 0) {
- res |= feature_names[i].value;
- break;
- }
- }
-
- if (i == ARRAY_SIZE (feature_names) && incompat_out) {
- /* Unrecognized feature */
- const char *have = strchr (flags, mode);
- if (have && have < features + llen) {
- /* This feature is required to access this database in
- * 'mode', but we don't understand it. */
- if (! *incompat_out)
- *incompat_out = talloc_strdup (ctx, "");
- *incompat_out = talloc_asprintf_append_buffer (
- *incompat_out, "%s%.*s", **incompat_out ? ", " : "",
- namelen, features);
- }
- }
- }
-
- return res;
-}
-
-static char *
-_print_features (const void *ctx, unsigned int features)
-{
- unsigned int i;
- char *res = talloc_strdup (ctx, "");
-
- for (i = 0; i < ARRAY_SIZE (feature_names); ++i)
- if (features & feature_names[i].value)
- res = talloc_asprintf_append_buffer (
- res, "%s\t%s\n", feature_names[i].name, feature_names[i].flags);
-
- return res;
-}
-
notmuch_status_t
notmuch_database_open (const char *path,
notmuch_database_mode_t mode,
strip_trailing (notmuch->path, '/');
- notmuch->mode = mode;
+ notmuch->writable_xapian_db = NULL;
notmuch->atomic_nesting = 0;
notmuch->view = 1;
try {
string last_mod;
if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {
- notmuch->xapian_db = new Xapian::WritableDatabase (xapian_path,
- DB_ACTION);
+ notmuch->writable_xapian_db = new Xapian::WritableDatabase (xapian_path,
+ DB_ACTION);
+ notmuch->xapian_db = notmuch->writable_xapian_db;
} else {
notmuch->xapian_db = new Xapian::Database (xapian_path);
}
" has a newer database format version (%u) than supported by this\n"
" version of notmuch (%u).\n",
notmuch_path, version, NOTMUCH_DATABASE_VERSION));
- notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
notmuch_database_destroy (notmuch);
notmuch = NULL;
status = NOTMUCH_STATUS_FILE_ERROR;
/* Check features. */
incompat_features = NULL;
- notmuch->features = _parse_features (
+ notmuch->features = _notmuch_database_parse_features (
local, notmuch->xapian_db->get_metadata ("features").c_str (),
version, mode == NOTMUCH_DATABASE_MODE_READ_WRITE ? 'w' : 'r',
&incompat_features);
" requires features (%s)\n"
" not supported by this version of notmuch.\n",
notmuch_path, incompat_features));
- notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
notmuch_database_destroy (notmuch);
notmuch = NULL;
status = NOTMUCH_STATUS_FILE_ERROR;
notmuch->query_parser = new Xapian::QueryParser;
notmuch->term_gen = new Xapian::TermGenerator;
notmuch->term_gen->set_stemmer (Xapian::Stem ("english"));
- notmuch->value_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
- notmuch->date_range_processor = new ParseTimeValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
- notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
-
+ notmuch->value_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
+ notmuch->date_range_processor = new ParseTimeRangeProcessor (NOTMUCH_VALUE_TIMESTAMP, "date:");
+ notmuch->last_mod_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
notmuch->query_parser->set_default_op (Xapian::Query::OP_AND);
notmuch->query_parser->set_database (*notmuch->xapian_db);
notmuch->query_parser->set_stemmer (Xapian::Stem ("english"));
notmuch->query_parser->set_stemming_strategy (Xapian::QueryParser::STEM_SOME);
- notmuch->query_parser->add_valuerangeprocessor (notmuch->value_range_processor);
- notmuch->query_parser->add_valuerangeprocessor (notmuch->date_range_processor);
- notmuch->query_parser->add_valuerangeprocessor (notmuch->last_mod_range_processor);
+ notmuch->query_parser->add_rangeprocessor (notmuch->value_range_processor);
+ notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor);
+ notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor);
for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
const prefix_t *prefix = &prefix_table[i];
*database = notmuch;
else
talloc_free (notmuch);
+
+ if (notmuch)
+ notmuch->open = true;
+
return status;
}
/* Many Xapian objects (and thus notmuch objects) hold references to
* the database, so merely deleting the database may not suffice to
* close it. Thus, we explicitly close it here. */
- if (notmuch->xapian_db != NULL) {
+ if (notmuch->open) {
try {
/* If there's an outstanding transaction, it's unclear if
* closing the Xapian database commits everything up to
* that transaction, or may discard committed (but
* unflushed) transactions. To be certain, explicitly
* cancel any outstanding transaction before closing. */
- if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_WRITE &&
+ if (_notmuch_database_mode (notmuch) == NOTMUCH_DATABASE_MODE_READ_WRITE &&
notmuch->atomic_nesting)
- (static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db))
- ->cancel_transaction ();
+ notmuch->writable_xapian_db->cancel_transaction ();
/* Close the database. This implicitly flushes
* outstanding changes. */
}
}
}
-
- delete notmuch->term_gen;
- notmuch->term_gen = NULL;
- delete notmuch->query_parser;
- notmuch->query_parser = NULL;
- delete notmuch->xapian_db;
- notmuch->xapian_db = NULL;
- delete notmuch->value_range_processor;
- notmuch->value_range_processor = NULL;
- delete notmuch->date_range_processor;
- notmuch->date_range_processor = NULL;
- delete notmuch->last_mod_range_processor;
- notmuch->last_mod_range_processor = NULL;
-
+ notmuch->open = false;
return status;
}
notmuch_status_t
_notmuch_database_reopen (notmuch_database_t *notmuch)
{
- if (notmuch->mode != NOTMUCH_DATABASE_MODE_READ_ONLY)
+ if (_notmuch_database_mode (notmuch) != NOTMUCH_DATABASE_MODE_READ_ONLY)
return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
try {
try {
NotmuchCompactor compactor (status_cb, closure);
-
- compactor.set_renumber (false);
- compactor.add_source (xapian_path);
- compactor.set_destdir (compact_xapian_path);
- compactor.compact ();
+ notmuch->xapian_db->compact (compact_xapian_path, Xapian::DBCOMPACT_NO_RENUMBER, 0, compactor);
} catch (const Xapian::Error &error) {
_notmuch_database_log (notmuch, "Error while compacting: %s\n", error.get_msg ().c_str ());
ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
notmuch_status_t status;
status = notmuch_database_close (notmuch);
+
+ delete notmuch->term_gen;
+ notmuch->term_gen = NULL;
+ delete notmuch->query_parser;
+ notmuch->query_parser = NULL;
+ delete notmuch->xapian_db;
+ notmuch->xapian_db = NULL;
+ delete notmuch->value_range_processor;
+ notmuch->value_range_processor = NULL;
+ delete notmuch->date_range_processor;
+ notmuch->date_range_processor = NULL;
+ delete notmuch->last_mod_range_processor;
+ notmuch->last_mod_range_processor = NULL;
+
talloc_free (notmuch);
return status;
const char *str;
char *end;
- version_string = notmuch->xapian_db->get_metadata ("version");
+ try {
+ version_string = notmuch->xapian_db->get_metadata ("version");
+ } catch (const Xapian::Error &error) {
+ LOG_XAPIAN_EXCEPTION (notmuch, error);
+ return 0;
+ }
+
if (version_string.empty ())
return 0;
notmuch_bool_t
notmuch_database_needs_upgrade (notmuch_database_t *notmuch)
{
- return notmuch->mode == NOTMUCH_DATABASE_MODE_READ_WRITE &&
- ((NOTMUCH_FEATURES_CURRENT & ~notmuch->features) ||
- (notmuch_database_get_version (notmuch) < NOTMUCH_DATABASE_VERSION));
+ unsigned int version;
+
+ if (_notmuch_database_mode (notmuch) != NOTMUCH_DATABASE_MODE_READ_WRITE)
+ return FALSE;
+
+ if (NOTMUCH_FEATURES_CURRENT & ~notmuch->features)
+ return TRUE;
+
+ version = notmuch_database_get_version (notmuch);
+
+ return (version > 0 && version < NOTMUCH_DATABASE_VERSION);
}
static volatile sig_atomic_t do_progress_notify = 0;
*/
notmuch_status_t
notmuch_database_upgrade (notmuch_database_t *notmuch,
- void (*progress_notify)(void *closure,
- double progress),
+ void (*progress_notify) (void *closure,
+ double progress),
void *closure)
{
void *local = talloc_new (NULL);
if (status)
return status;
- db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
+ db = notmuch->writable_xapian_db;
target_features = notmuch->features | NOTMUCH_FEATURES_CURRENT;
new_features = NOTMUCH_FEATURES_CURRENT & ~notmuch->features;
mtime = Xapian::sortable_unserialise (
document.get_value (NOTMUCH_VALUE_TIMESTAMP));
- directory = _notmuch_directory_create (notmuch, term.c_str () + 10,
- NOTMUCH_FIND_CREATE, &status);
+ directory = _notmuch_directory_find_or_create (notmuch, term.c_str () + 10,
+ NOTMUCH_FIND_CREATE, &status);
notmuch_directory_set_mtime (directory, mtime);
notmuch_directory_destroy (directory);
}
status = NOTMUCH_STATUS_SUCCESS;
- db->set_metadata ("features", _print_features (local, notmuch->features));
+ db->set_metadata ("features", _notmuch_database_print_features (local, notmuch->features));
db->set_metadata ("version", STRINGIFY (NOTMUCH_DATABASE_VERSION));
DONE:
notmuch_status_t
notmuch_database_begin_atomic (notmuch_database_t *notmuch)
{
- if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY ||
+ if (_notmuch_database_mode (notmuch) == NOTMUCH_DATABASE_MODE_READ_ONLY ||
notmuch->atomic_nesting > 0)
goto DONE;
return NOTMUCH_STATUS_UPGRADE_REQUIRED;
try {
- (static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db))->begin_transaction (false);
+ notmuch->writable_xapian_db->begin_transaction (false);
} catch (const Xapian::Error &error) {
_notmuch_database_log (notmuch, "A Xapian exception occurred beginning transaction: %s.\n",
error.get_msg ().c_str ());
if (notmuch->atomic_nesting == 0)
return NOTMUCH_STATUS_UNBALANCED_ATOMIC;
- if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY ||
+ if (_notmuch_database_mode (notmuch) == NOTMUCH_DATABASE_MODE_READ_ONLY ||
notmuch->atomic_nesting > 1)
goto DONE;
- db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
+ db = notmuch->writable_xapian_db;
try {
db->commit_transaction ();
return NOTMUCH_STATUS_SUCCESS;
}
- directory = _notmuch_directory_create (notmuch, path, flags, &status);
+ directory = _notmuch_directory_find_or_create (notmuch, path, flags, &status);
if (status || ! directory) {
*directory_id = -1;
return status;
*directory = NULL;
try {
- *directory = _notmuch_directory_create (notmuch, path,
- NOTMUCH_FIND_LOOKUP, &status);
+ *directory = _notmuch_directory_find_or_create (notmuch, path,
+ NOTMUCH_FIND_LOOKUP, &status);
} catch (const Xapian::Error &error) {
_notmuch_database_log (notmuch, "A Xapian exception occurred getting directory: %s.\n",
error.get_msg ().c_str ());