+ notmuch->query_parser->add_valuerangeprocessor (notmuch->date_range_processor);
+ notmuch->query_parser->add_valuerangeprocessor (notmuch->last_mod_range_processor);
+
+ for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
+ const prefix_t *prefix = &prefix_table[i];
+ if (prefix->flags & NOTMUCH_FIELD_EXTERNAL) {
+ if (prefix->flags & NOTMUCH_FIELD_PROBABILISTIC) {
+ notmuch->query_parser->add_prefix (prefix->name, prefix->prefix);
+ } else {
+ notmuch->query_parser->add_boolean_prefix (prefix->name,
+ prefix->prefix);
+ }
+ }
+ }
+ } catch (const Xapian::Error &error) {
+ IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n",
+ error.get_msg().c_str()));
+ notmuch_database_destroy (notmuch);
+ notmuch = NULL;
+ status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ }
+
+ DONE:
+ talloc_free (local);
+
+ if (message) {
+ if (status_string)
+ *status_string = message;
+ else
+ free (message);
+ }
+
+ if (database)
+ *database = notmuch;
+ else
+ talloc_free (notmuch);
+ return status;
+}
+
+notmuch_status_t
+notmuch_database_close (notmuch_database_t *notmuch)
+{
+ notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+
+ /* 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) {
+ 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 &&
+ notmuch->atomic_nesting)
+ (static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db))
+ ->cancel_transaction ();
+
+ /* Close the database. This implicitly flushes
+ * outstanding changes. */
+ notmuch->xapian_db->close();
+ } catch (const Xapian::Error &error) {
+ status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ if (! notmuch->exception_reported) {
+ _notmuch_database_log (notmuch, "Error: A Xapian exception occurred closing database: %s\n",
+ error.get_msg().c_str());
+ }
+ }
+ }
+
+ 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;
+
+ return status;
+}
+
+notmuch_status_t
+_notmuch_database_reopen (notmuch_database_t *notmuch)
+{
+ if (notmuch->mode != NOTMUCH_DATABASE_MODE_READ_ONLY)
+ return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
+
+ try {
+ notmuch->xapian_db->reopen ();
+ } catch (const Xapian::Error &error) {
+ if (! notmuch->exception_reported) {
+ _notmuch_database_log (notmuch, "Error: A Xapian exception reopening database: %s\n",
+ error.get_msg ().c_str ());
+ notmuch->exception_reported = TRUE;
+ }
+ return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ }
+
+ notmuch->view++;
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+static int
+unlink_cb (const char *path,
+ unused (const struct stat *sb),
+ unused (int type),
+ unused (struct FTW *ftw))
+{
+ return remove (path);
+}
+
+static int
+rmtree (const char *path)
+{
+ return nftw (path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
+}
+
+class NotmuchCompactor : public Xapian::Compactor
+{
+ notmuch_compact_status_cb_t status_cb;
+ void *status_closure;
+
+public:
+ NotmuchCompactor(notmuch_compact_status_cb_t cb, void *closure) :
+ status_cb (cb), status_closure (closure) { }
+
+ virtual void
+ set_status (const std::string &table, const std::string &status)
+ {
+ char *msg;
+
+ if (status_cb == NULL)
+ return;