]> git.notmuchmail.org Git - notmuch/blobdiff - lib/database.cc
Merge branch 'release'
[notmuch] / lib / database.cc
index 86bf26192079bcbcf9f5475556d0ad71be2fef0b..386dcd17adc4d0a91ab5eb10479907b254d32187 100644 (file)
@@ -42,6 +42,7 @@ using namespace std;
 typedef struct {
     const char *name;
     const char *prefix;
+    notmuch_field_flag_t flags;
 } prefix_t;
 
 #define NOTMUCH_DATABASE_VERSION 3
@@ -49,6 +50,12 @@ typedef struct {
 #define STRINGIFY(s) _SUB_STRINGIFY(s)
 #define _SUB_STRINGIFY(s) #s
 
+#if HAVE_XAPIAN_DB_RETRY_LOCK
+#define DB_ACTION (Xapian::DB_CREATE_OR_OPEN | Xapian::DB_RETRY_LOCK)
+#else
+#define DB_ACTION Xapian::DB_CREATE_OR_OPEN
+#endif
+
 /* Here's the current schema for our database (for NOTMUCH_DATABASE_VERSION):
  *
  * We currently have three different types of documents (mail, ghost,
@@ -91,6 +98,9 @@ typedef struct {
  *                     STRING is the name of a file within that
  *                     directory for this mail message.
  *
+ *      property:       Has a property with key=value
+ *                 FIXME: if no = is present, should match on any value
+ *
  *    A mail document also has four values:
  *
  *     TIMESTAMP:      The time_t value corresponding to the message's
@@ -185,6 +195,14 @@ typedef struct {
  *                     generated is 1 and the value will be
  *                     incremented for each thread ID.
  *
+ *     C*              metadata keys starting with C indicate
+ *                     configuration data. It can be managed with the
+ *                     n_database_*config* API.  There is a convention
+ *                     of hierarchical keys separated by '.' (e.g.
+ *                     query.notmuch stores the value for the named
+ *                     query 'notmuch'), but it is not enforced by the
+ *                     API.
+ *
  * Obsolete metadata
  * -----------------
  *
@@ -230,36 +248,38 @@ typedef struct {
  * nearly universal to all mail messages).
  */
 
-static prefix_t BOOLEAN_PREFIX_INTERNAL[] = {
-    { "type",                  "T" },
-    { "reference",             "XREFERENCE" },
-    { "replyto",               "XREPLYTO" },
-    { "directory",             "XDIRECTORY" },
-    { "file-direntry",         "XFDIRENTRY" },
-    { "directory-direntry",    "XDDIRENTRY" },
-};
-
-static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = {
-    { "thread",                        "G" },
-    { "tag",                   "K" },
-    { "is",                    "K" },
-    { "id",                    "Q" },
-    { "path",                  "P" },
+static const
+prefix_t prefix_table[] = {
+    /* name                    term prefix     flags */
+    { "type",                  "T",            NOTMUCH_FIELD_NO_FLAGS },
+    { "reference",             "XREFERENCE",   NOTMUCH_FIELD_NO_FLAGS },
+    { "replyto",               "XREPLYTO",     NOTMUCH_FIELD_NO_FLAGS },
+    { "directory",             "XDIRECTORY",   NOTMUCH_FIELD_NO_FLAGS },
+    { "file-direntry",         "XFDIRENTRY",   NOTMUCH_FIELD_NO_FLAGS },
+    { "directory-direntry",    "XDDIRENTRY",   NOTMUCH_FIELD_NO_FLAGS },
+    { "thread",                        "G",            NOTMUCH_FIELD_EXTERNAL },
+    { "tag",                   "K",            NOTMUCH_FIELD_EXTERNAL },
+    { "is",                    "K",            NOTMUCH_FIELD_EXTERNAL },
+    { "id",                    "Q",            NOTMUCH_FIELD_EXTERNAL },
+    { "path",                  "P",            NOTMUCH_FIELD_EXTERNAL },
+    { "property",              "XPROPERTY",    NOTMUCH_FIELD_EXTERNAL },
     /*
-     * Without the ":", since this is a multi-letter prefix, Xapian
-     * will add a colon itself if the first letter of the path is
-     * upper-case ASCII. Including the ":" forces there to always be a
-     * colon, which keeps our own logic simpler.
+     * Unconditionally add ':' to reduce potential ambiguity with
+     * overlapping prefixes and/or terms that start with capital
+     * letters. See Xapian document termprefixes.html for related
+     * discussion.
      */
-    { "folder",                        "XFOLDER:" },
-};
-
-static prefix_t PROBABILISTIC_PREFIX[]= {
-    { "from",                  "XFROM" },
-    { "to",                    "XTO" },
-    { "attachment",            "XATTACHMENT" },
-    { "mimetype",              "XMIMETYPE"},
-    { "subject",               "XSUBJECT"},
+    { "folder",                        "XFOLDER:",     NOTMUCH_FIELD_EXTERNAL },
+    { "from",                  "XFROM",        NOTMUCH_FIELD_EXTERNAL |
+                                               NOTMUCH_FIELD_PROBABILISTIC },
+    { "to",                    "XTO",          NOTMUCH_FIELD_EXTERNAL |
+                                               NOTMUCH_FIELD_PROBABILISTIC },
+    { "attachment",            "XATTACHMENT",  NOTMUCH_FIELD_EXTERNAL |
+                                               NOTMUCH_FIELD_PROBABILISTIC },
+    { "mimetype",              "XMIMETYPE",    NOTMUCH_FIELD_EXTERNAL |
+                                               NOTMUCH_FIELD_PROBABILISTIC },
+    { "subject",               "XSUBJECT",     NOTMUCH_FIELD_EXTERNAL |
+                                               NOTMUCH_FIELD_PROBABILISTIC },
 };
 
 const char *
@@ -267,19 +287,9 @@ _find_prefix (const char *name)
 {
     unsigned int i;
 
-    for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_INTERNAL); i++) {
-       if (strcmp (name, BOOLEAN_PREFIX_INTERNAL[i].name) == 0)
-           return BOOLEAN_PREFIX_INTERNAL[i].prefix;
-    }
-
-    for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_EXTERNAL); i++) {
-       if (strcmp (name, BOOLEAN_PREFIX_EXTERNAL[i].name) == 0)
-           return BOOLEAN_PREFIX_EXTERNAL[i].prefix;
-    }
-
-    for (i = 0; i < ARRAY_SIZE (PROBABILISTIC_PREFIX); i++) {
-       if (strcmp (name, PROBABILISTIC_PREFIX[i].name) == 0)
-           return PROBABILISTIC_PREFIX[i].prefix;
+    for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
+       if (strcmp (name, prefix_table[i].name) == 0)
+           return prefix_table[i].prefix;
     }
 
     INTERNAL_ERROR ("No prefix exists for '%s'\n", name);
@@ -369,6 +379,22 @@ _notmuch_database_log (notmuch_database_t *notmuch,
        talloc_free (notmuch->status_string);
 
     notmuch->status_string = talloc_vasprintf (notmuch, format, va_args);
+    va_end (va_args);
+}
+
+void
+_notmuch_database_log_append (notmuch_database_t *notmuch,
+                     const char *format,
+                     ...)
+{
+    va_list va_args;
+
+    va_start (va_args, format);
+
+    if (notmuch->status_string)
+       notmuch->status_string = talloc_vasprintf_append (notmuch->status_string, format, va_args);
+    else
+       notmuch->status_string = talloc_vasprintf (notmuch, format, va_args);
 
     va_end (va_args);
 }
@@ -618,7 +644,7 @@ parse_references (void *ctx,
        ref = _parse_message_id (ctx, refs, &refs);
 
        if (ref && strcmp (ref, message_id)) {
-           g_hash_table_insert (hash, ref, NULL);
+           g_hash_table_add (hash, ref);
            last_ref = ref;
        }
     }
@@ -627,7 +653,7 @@ parse_references (void *ctx,
      * reference to the database.  We should avoid making a message
      * its own parent, thus the above check.
      */
-    return last_ref;
+    return talloc_strdup(ctx, last_ref);
 }
 
 notmuch_status_t
@@ -931,7 +957,7 @@ notmuch_database_open_verbose (const char *path,
 
        if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {
            notmuch->xapian_db = new Xapian::WritableDatabase (xapian_path,
-                                                              Xapian::DB_CREATE_OR_OPEN);
+                                                              DB_ACTION);
        } else {
            notmuch->xapian_db = new Xapian::Database (xapian_path);
        }
@@ -1004,10 +1030,14 @@ notmuch_database_open_verbose (const char *path,
 #if HAVE_XAPIAN_FIELD_PROCESSOR
        /* This currently relies on the query parser to pass anything
         * with a .. to the range processor */
-       notmuch->date_field_processor = new DateFieldProcessor();
-       notmuch->query_parser->add_boolean_prefix("date", notmuch->date_field_processor);
-       notmuch->query_field_processor = new QueryFieldProcessor (*notmuch->query_parser, notmuch);
-       notmuch->query_parser->add_boolean_prefix("query", notmuch->query_field_processor);
+       {
+           Xapian::FieldProcessor * date_fp = new DateFieldProcessor();
+           Xapian::FieldProcessor * query_fp =
+               new QueryFieldProcessor (*notmuch->query_parser, notmuch);
+
+           notmuch->query_parser->add_boolean_prefix("date", date_fp->release ());
+           notmuch->query_parser->add_boolean_prefix("query", query_fp->release ());
+       }
 #endif
        notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
 
@@ -1019,15 +1049,16 @@ notmuch_database_open_verbose (const char *path,
        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 (BOOLEAN_PREFIX_EXTERNAL); i++) {
-           prefix_t *prefix = &BOOLEAN_PREFIX_EXTERNAL[i];
-           notmuch->query_parser->add_boolean_prefix (prefix->name,
-                                                      prefix->prefix);
-       }
-
-       for (i = 0; i < ARRAY_SIZE (PROBABILISTIC_PREFIX); i++) {
-           prefix_t *prefix = &PROBABILISTIC_PREFIX[i];
-           notmuch->query_parser->add_prefix (prefix->name, prefix->prefix);
+       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",
@@ -1102,7 +1133,6 @@ notmuch_database_close (notmuch_database_t *notmuch)
     return status;
 }
 
-#if HAVE_XAPIAN_COMPACT
 static int
 unlink_cb (const char *path,
           unused (const struct stat *sb),
@@ -1286,17 +1316,6 @@ notmuch_database_compact (const char *path,
 
     return ret;
 }
-#else
-notmuch_status_t
-notmuch_database_compact (unused (const char *path),
-                         unused (const char *backup_path),
-                         unused (notmuch_compact_status_cb_t status_cb),
-                         unused (void *closure))
-{
-    _notmuch_database_log (notmuch, "notmuch was compiled against a xapian version lacking compaction support.\n");
-    return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
-}
-#endif
 
 notmuch_status_t
 notmuch_database_destroy (notmuch_database_t *notmuch)
@@ -1644,8 +1663,8 @@ notmuch_database_begin_atomic (notmuch_database_t *notmuch)
        notmuch->atomic_nesting > 0)
        goto DONE;
 
-       if (notmuch_database_needs_upgrade(notmuch))
-               return NOTMUCH_STATUS_UPGRADE_REQUIRED;
+    if (notmuch_database_needs_upgrade (notmuch))
+       return NOTMUCH_STATUS_UPGRADE_REQUIRED;
 
     try {
        (static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db))->begin_transaction (false);
@@ -1682,7 +1701,7 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch)
         * However, we rely on flushing to test atomicity. */
        const char *thresh = getenv ("XAPIAN_FLUSH_THRESHOLD");
        if (thresh && atoi (thresh) == 1)
-           db->flush ();
+           db->commit ();
     } catch (const Xapian::Error &error) {
        _notmuch_database_log (notmuch, "A Xapian exception occurred committing transaction: %s.\n",
                 error.get_msg().c_str());