database: Store mail filename as a new 'direntry' term, not as 'data'.
authorCarl Worth <cworth@cworth.org>
Mon, 21 Dec 2009 16:23:26 +0000 (08:23 -0800)
committerCarl Worth <cworth@cworth.org>
Wed, 6 Jan 2010 18:32:05 +0000 (10:32 -0800)
Instead of storing the complete message filename in the data portion
of a mail document we now store a 'direntry' term that contains the
document ID of a directory document and also the basename of the
message filename within that directory. This will allow us to easily
store multple filenames for a single message, and will also allow us
to find mail documents for files that previously existed in a
directory but that have since been deleted.

lib/database.cc
lib/index.cc
lib/message.cc
lib/notmuch-private.h

index f122c2e4b52f70115052aa963fb4783726641599..7d09119cecddf54a94e34b8f2a9948ee203fbb85 100644 (file)
@@ -63,6 +63,11 @@ typedef struct {
  *
  *     tag:       Any tags associated with this message by the user.
  *
+ *     direntry:  A colon-separated pair of values (INTEGER:STRING),
+ *                where INTEGER is the document ID of a directory
+ *                document, and STRING is the name of a file within
+ *                that directory for this mail message.
+ *
  *    A mail document also has two values:
  *
  *     TIMESTAMP:      The time_t value corresponding to the message's
@@ -75,8 +80,7 @@ typedef struct {
  * user in searching. But the database doesn't really care itself
  * about any of these.
  *
- * Finally, the data portion of a mail document contains the path name
- * of the mail message (relative to the database path).
+ * The data portion of a mail document is empty.
  *
  * Directory document
  * ------------------
@@ -122,6 +126,7 @@ prefix_t BOOLEAN_PREFIX_INTERNAL[] = {
     { "reference", "XREFERENCE" },
     { "replyto", "XREPLYTO" },
     { "directory", "XDIRECTORY" },
+    { "direntry", "XDIRENTRY" },
     { "parent", "XPARENT" },
 };
 
@@ -717,6 +722,18 @@ _notmuch_database_find_directory_id (notmuch_database_t *notmuch,
     return status;
 }
 
+const char *
+_notmuch_database_get_directory_path (void *ctx,
+                                     notmuch_database_t *notmuch,
+                                     unsigned int doc_id)
+{
+    Xapian::Document document;
+
+    document = find_document_for_doc_id (notmuch, doc_id);
+
+    return talloc_strdup (ctx, document.get_data ().c_str ());
+}
+
 /* Given a legal 'path' for the database, return the relative path.
  *
  * The return value will be a pointer to the originl path contents,
@@ -812,6 +829,8 @@ notmuch_database_set_directory_mtime (notmuch_database_t *notmuch,
     if (db_path != path)
        free ((char *) db_path);
 
+    talloc_free (local);
+
     return ret;
 }
 
@@ -844,8 +863,6 @@ notmuch_database_get_directory_mtime (notmuch_database_t *notmuch,
     if (db_path != path)
        free ((char *) db_path);
 
-    talloc_free (local);
-
     return ret;
 }
 
index 125fa6c94f2b6612f6aac3e36dfe518753eaddaf..e58bc8706053e0c00c4e59d43d54fad2a189262f 100644 (file)
@@ -31,7 +31,7 @@ _index_address_mailbox (notmuch_message_t *message,
 {
     InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address);
     const char *name, *addr;
-    void *local = talloc_new (NULL);
+    void *local = talloc_new (message);
 
     name = internet_address_get_name (address);
     addr = internet_address_mailbox_get_addr (mailbox);
index 7c7ea7a1c02aa84d47e9ff8f3db82216d9aedbce..3f5334239e0839d5d0bcae3595e4e28b7f1b5280 100644 (file)
@@ -392,11 +392,15 @@ notmuch_message_get_replies (notmuch_message_t *message)
  *
  * This change will not be reflected in the database until the next
  * call to _notmuch_message_set_sync. */
-void
+notmuch_status_t
 _notmuch_message_set_filename (notmuch_message_t *message,
                               const char *filename)
 {
-    const char *relative;
+    const char *relative, *directory, *basename;
+    char *term;
+    Xapian::docid directory_id;
+    notmuch_status_t status;
+    void *local = talloc_new (message);
 
     if (message->filename) {
        talloc_free (message->filename);
@@ -407,26 +411,79 @@ _notmuch_message_set_filename (notmuch_message_t *message,
        INTERNAL_ERROR ("Message filename cannot be NULL.");
 
     relative = _notmuch_database_relative_path (message->notmuch, filename);
-    message->doc.set_data (relative);
+
+    status = _notmuch_database_split_path (local, relative,
+                                          &directory, &basename);
+    if (status)
+       return status;
+
+    status = _notmuch_database_find_directory_id (message->notmuch, directory,
+                                                 &directory_id);
+    if (status)
+       return status;
+
+    term = talloc_asprintf (local, "%s%u:%s",
+                           _find_prefix ("direntry"), directory_id, basename);
+
+    message->doc.add_term (term);
+
+    talloc_free (local);
+
+    return NOTMUCH_STATUS_SUCCESS;
 }
 
 const char *
 notmuch_message_get_filename (notmuch_message_t *message)
 {
-    std::string filename_str;
-    const char *db_path;
+    const char *prefix = _find_prefix ("direntry");
+    int prefix_len = strlen (prefix);
+    Xapian::TermIterator i;
+    char *direntry, *colon;
+    const char *db_path, *directory, *basename;
+    unsigned int directory_id;
+    void *local = talloc_new (message);
 
     if (message->filename)
        return message->filename;
 
-    filename_str = message->doc.get_data ();
+    i = message->doc.termlist_begin ();
+    i.skip_to (prefix);
+
+    if (i != message->doc.termlist_end ())
+       direntry = talloc_strdup (local, (*i).c_str ());
+
+    if (i == message->doc.termlist_end () ||
+       strncmp (direntry, prefix, prefix_len))
+    {
+       INTERNAL_ERROR ("message with no filename");
+    }
+
+    direntry += prefix_len;
+
+    directory_id = strtol (direntry, &colon, 10);
+
+    if (colon == NULL || *colon != ':')
+       INTERNAL_ERROR ("malformed direntry");
+
+    basename = colon + 1;
+
+    *colon = '\0';
+
     db_path = notmuch_database_get_path (message->notmuch);
 
-    if (filename_str[0] != '/')
-       message->filename = talloc_asprintf (message, "%s/%s", db_path,
-                                            filename_str.c_str ());
+    directory = _notmuch_database_get_directory_path (local,
+                                                     message->notmuch,
+                                                     directory_id);
+
+    if (strlen (directory))
+       message->filename = talloc_asprintf (message, "%s/%s/%s",
+                                            db_path, directory, basename);
     else
-       message->filename = talloc_strdup (message, filename_str.c_str ());
+       message->filename = talloc_asprintf (message, "%s/%s",
+                                            db_path, basename);
+    talloc_free ((void *) directory);
+
+    talloc_free (local);
 
     return message->filename;
 }
index 9f470c9f9e389d9ff19c6ceced0ad08bcddd724e..5a930bb32a87f0204a3f13c75ea5071b0f9b8136 100644 (file)
@@ -166,6 +166,11 @@ _notmuch_database_find_directory_id (notmuch_database_t *database,
                                     const char *path,
                                     unsigned int *directory_id);
 
+const char *
+_notmuch_database_get_directory_path (void *ctx,
+                                     notmuch_database_t *notmuch,
+                                     unsigned int doc_id);
+
 /* thread.cc */
 
 notmuch_thread_t *
@@ -205,7 +210,7 @@ _notmuch_message_gen_terms (notmuch_message_t *message,
                            const char *prefix_name,
                            const char *text);
 
-void
+notmuch_status_t
 _notmuch_message_set_filename (notmuch_message_t *message,
                               const char *filename);