#include "notmuch-private.h"
#include "database-private.h"
+#include "message-private.h"
#include <stdint.h>
notmuch_string_list_t *filename_list;
char *author;
notmuch_message_file_t *message_file;
+ notmuch_string_list_t *property_term_list;
+ notmuch_string_map_t *property_map;
notmuch_message_list_t *replies;
unsigned long flags;
/* For flags that are initialized on-demand, lazy_flags indicates
/* Message document modified since last sync */
notmuch_bool_t modified;
+ /* last view of database the struct is synced with */
+ unsigned long last_view;
+
Xapian::Document doc;
Xapian::termcount termpos;
};
message->flags = 0;
message->lazy_flags = 0;
+ /* the message is initially not synchronized with Xapian */
+ message->last_view = 0;
+
/* Each of these will be lazily created as needed. */
message->message_id = NULL;
message->thread_id = NULL;
message->filename_list = NULL;
message->message_file = NULL;
message->author = NULL;
+ message->property_term_list = NULL;
+ message->property_map = NULL;
message->replies = _notmuch_message_list_create (message);
if (unlikely (message->replies == NULL)) {
return value;
}
-void
-_notmuch_message_ensure_metadata (notmuch_message_t *message)
+static void
+_notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
{
Xapian::TermIterator i, end;
+
+ if (field && (message->last_view >= message->notmuch->view))
+ return;
+
const char *thread_prefix = _find_prefix ("thread"),
*tag_prefix = _find_prefix ("tag"),
*id_prefix = _find_prefix ("id"),
*type_prefix = _find_prefix ("type"),
*filename_prefix = _find_prefix ("file-direntry"),
+ *property_prefix = _find_prefix ("property"),
*replyto_prefix = _find_prefix ("replyto");
/* We do this all in a single pass because Xapian decompresses the
* slightly more costly than looking up individual fields if only
* one field of the message object is actually used, it's a huge
* win as more fields are used. */
+ for (int count=0; count < 3; count++) {
+ try {
+ i = message->doc.termlist_begin ();
+ end = message->doc.termlist_end ();
+
+ /* Get thread */
+ if (!message->thread_id)
+ message->thread_id =
+ _notmuch_message_get_term (message, i, end, thread_prefix);
+
+ /* Get tags */
+ assert (strcmp (thread_prefix, tag_prefix) < 0);
+ if (!message->tag_list) {
+ message->tag_list =
+ _notmuch_database_get_terms_with_prefix (message, i, end,
+ tag_prefix);
+ _notmuch_string_list_sort (message->tag_list);
+ }
- i = message->doc.termlist_begin ();
- end = message->doc.termlist_end ();
+ /* Get id */
+ assert (strcmp (tag_prefix, id_prefix) < 0);
+ if (!message->message_id)
+ message->message_id =
+ _notmuch_message_get_term (message, i, end, id_prefix);
+
+ /* Get document type */
+ assert (strcmp (id_prefix, type_prefix) < 0);
+ if (! NOTMUCH_TEST_BIT (message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST)) {
+ i.skip_to (type_prefix);
+ /* "T" is the prefix "type" fields. See
+ * BOOLEAN_PREFIX_INTERNAL. */
+ if (*i == "Tmail")
+ NOTMUCH_CLEAR_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
+ else if (*i == "Tghost")
+ NOTMUCH_SET_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
+ else
+ INTERNAL_ERROR ("Message without type term");
+ NOTMUCH_SET_BIT (&message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST);
+ }
- /* Get thread */
- if (!message->thread_id)
- message->thread_id =
- _notmuch_message_get_term (message, i, end, thread_prefix);
-
- /* Get tags */
- assert (strcmp (thread_prefix, tag_prefix) < 0);
- if (!message->tag_list) {
- message->tag_list =
- _notmuch_database_get_terms_with_prefix (message, i, end,
- tag_prefix);
- _notmuch_string_list_sort (message->tag_list);
- }
+ /* Get filename list. Here we get only the terms. We lazily
+ * expand them to full file names when needed in
+ * _notmuch_message_ensure_filename_list. */
+ assert (strcmp (type_prefix, filename_prefix) < 0);
+ if (!message->filename_term_list && !message->filename_list)
+ message->filename_term_list =
+ _notmuch_database_get_terms_with_prefix (message, i, end,
+ filename_prefix);
+
+
+ /* Get property terms. Mimic the setup with filenames above */
+ assert (strcmp (filename_prefix, property_prefix) < 0);
+ if (!message->property_map && !message->property_term_list)
+ message->property_term_list =
+ _notmuch_database_get_terms_with_prefix (message, i, end,
+ property_prefix);
+
+ /* Get reply to */
+ assert (strcmp (property_prefix, replyto_prefix) < 0);
+ if (!message->in_reply_to)
+ message->in_reply_to =
+ _notmuch_message_get_term (message, i, end, replyto_prefix);
- /* Get id */
- assert (strcmp (tag_prefix, id_prefix) < 0);
- if (!message->message_id)
- message->message_id =
- _notmuch_message_get_term (message, i, end, id_prefix);
-
- /* Get document type */
- assert (strcmp (id_prefix, type_prefix) < 0);
- if (! NOTMUCH_TEST_BIT (message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST)) {
- i.skip_to (type_prefix);
- /* "T" is the prefix "type" fields. See
- * BOOLEAN_PREFIX_INTERNAL. */
- if (*i == "Tmail")
- NOTMUCH_CLEAR_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
- else if (*i == "Tghost")
- NOTMUCH_SET_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST);
- else
- INTERNAL_ERROR ("Message without type term");
- NOTMUCH_SET_BIT (&message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST);
- }
- /* Get filename list. Here we get only the terms. We lazily
- * expand them to full file names when needed in
- * _notmuch_message_ensure_filename_list. */
- assert (strcmp (type_prefix, filename_prefix) < 0);
- if (!message->filename_term_list && !message->filename_list)
- message->filename_term_list =
- _notmuch_database_get_terms_with_prefix (message, i, end,
- filename_prefix);
-
- /* Get reply to */
- assert (strcmp (filename_prefix, replyto_prefix) < 0);
- if (!message->in_reply_to)
- message->in_reply_to =
- _notmuch_message_get_term (message, i, end, replyto_prefix);
- /* It's perfectly valid for a message to have no In-Reply-To
- * header. For these cases, we return an empty string. */
- if (!message->in_reply_to)
- message->in_reply_to = talloc_strdup (message, "");
+ /* It's perfectly valid for a message to have no In-Reply-To
+ * header. For these cases, we return an empty string. */
+ if (!message->in_reply_to)
+ message->in_reply_to = talloc_strdup (message, "");
+
+ /* all the way without an exception */
+ break;
+ } catch (const Xapian::DatabaseModifiedError &error) {
+ notmuch_status_t status = _notmuch_database_reopen (message->notmuch);
+ if (status != NOTMUCH_STATUS_SUCCESS)
+ INTERNAL_ERROR ("unhandled error from notmuch_database_reopen: %s\n",
+ notmuch_status_to_string (status));
+ } catch (const Xapian::Error &error) {
+ INTERNAL_ERROR ("A Xapian exception occurred fetching message metadata: %s\n",
+ error.get_msg().c_str());
+ }
+ }
+ message->last_view = message->notmuch->view;
}
-static void
+void
_notmuch_message_invalidate_metadata (notmuch_message_t *message,
const char *prefix_name)
{
message->filename_term_list = message->filename_list = NULL;
}
+ if (strcmp ("property", prefix_name) == 0) {
+
+ if (message->property_term_list)
+ talloc_free (message->property_term_list);
+ message->property_term_list = NULL;
+
+ if (message->property_map)
+ talloc_unlink (message, message->property_map);
+
+ message->property_map = NULL;
+ }
+
if (strcmp ("replyto", prefix_name) == 0) {
talloc_free (message->in_reply_to);
message->in_reply_to = NULL;
const char *
notmuch_message_get_message_id (notmuch_message_t *message)
{
- if (!message->message_id)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->message_id);
if (!message->message_id)
INTERNAL_ERROR ("Message with document ID of %u has no message ID.\n",
message->doc_id);
const char *
_notmuch_message_get_in_reply_to (notmuch_message_t *message)
{
- if (!message->in_reply_to)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->in_reply_to);
return message->in_reply_to;
}
const char *
notmuch_message_get_thread_id (notmuch_message_t *message)
{
- if (!message->thread_id)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->thread_id);
if (!message->thread_id)
INTERNAL_ERROR ("Message with document ID of %u has no thread ID.\n",
message->doc_id);
return _notmuch_messages_create (message->replies);
}
-static void
+void
_notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)
{
Xapian::TermIterator i;
if (message->filename_list)
return;
- if (!message->filename_term_list)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->filename_term_list);
message->filename_list = _notmuch_string_list_create (message);
node = message->filename_term_list->head;
*
* It would be nice to do the upgrade of the document directly
* here, but the database is likely open in read-only mode. */
- const char *data;
- data = message->doc.get_data ().c_str ();
+ std::string datastr = message->doc.get_data ();
+ const char *data = datastr.c_str ();
if (data == NULL)
INTERNAL_ERROR ("message with no filename");
{
if (flag == NOTMUCH_MESSAGE_FLAG_GHOST &&
! NOTMUCH_TEST_BIT (message->lazy_flags, flag))
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, NULL);
return NOTMUCH_TEST_BIT (message->flags, flag);
}
{
notmuch_tags_t *tags;
- if (!message->tag_list)
- _notmuch_message_ensure_metadata (message);
+ _notmuch_message_ensure_metadata (message, message->tag_list);
tags = _notmuch_tags_create (message, message->tag_list);
/* _notmuch_tags_create steals the reference to the tag_list, but
{
return message->notmuch;
}
+
+static void
+_notmuch_message_ensure_property_map (notmuch_message_t *message)
+{
+ notmuch_string_node_t *node;
+
+ if (message->property_map)
+ return;
+
+ _notmuch_message_ensure_metadata (message, message->property_term_list);
+
+ message->property_map = _notmuch_string_map_create (message);
+
+ for (node = message->property_term_list->head; node; node = node->next) {
+ const char *key;
+ char *value;
+
+ value = index(node->string, '=');
+ if (!value)
+ INTERNAL_ERROR ("malformed property term");
+
+ *value = '\0';
+ value++;
+ key = node->string;
+
+ _notmuch_string_map_append (message->property_map, key, value);
+
+ }
+
+ talloc_free (message->property_term_list);
+ message->property_term_list = NULL;
+}
+
+notmuch_string_map_t *
+_notmuch_message_property_map (notmuch_message_t *message)
+{
+ _notmuch_message_ensure_property_map (message);
+
+ return message->property_map;
+}
+
+notmuch_bool_t
+_notmuch_message_frozen (notmuch_message_t *message)
+{
+ return message->frozen;
+}