aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--TODO7
-rw-r--r--database.cc2
-rw-r--r--message.cc53
-rw-r--r--notmuch.c6
-rw-r--r--notmuch.h83
5 files changed, 136 insertions, 15 deletions
diff --git a/TODO b/TODO
index 8f9a1a98..baec040d 100644
--- a/TODO
+++ b/TODO
@@ -3,10 +3,3 @@ Write a "notmuch show" that displays a single thread.
Fix to use the *last* Message-ID header if multiple such headers are
encountered, (I noticed this is one thing that kept me from seeing the
same message-ID values as sup).
-
-Fix "notmuch restore" to delete the old tags from a message/thread
-before adding new ones. This will require someway to temporarily
-'disconnect' a notmuch_message_t from the database, (that it, disable
-automatic sync for add_tag, etc.), and then reconnect it. That is, the
-removal and subsequent addition of tags to the message/thread needs to
-be transactional.
diff --git a/database.cc b/database.cc
index ad91a7d7..9831d79c 100644
--- a/database.cc
+++ b/database.cc
@@ -166,6 +166,8 @@ notmuch_status_to_string (notmuch_status_t status)
return "Erroneous NULL pointer";
case NOTMUCH_STATUS_TAG_TOO_LONG:
return "Tag value is too long (exceeds NOTMUCH_TAG_MAX)";
+ case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:
+ return "Unblanced number of calls to notmuch_message_freeze/thaw";
default:
case NOTMUCH_STATUS_LAST_STATUS:
return "Unknown error status value";
diff --git a/message.cc b/message.cc
index 6b6141f3..6e15b511 100644
--- a/message.cc
+++ b/message.cc
@@ -26,6 +26,7 @@
struct _notmuch_message {
notmuch_database_t *notmuch;
Xapian::docid doc_id;
+ int frozen;
char *message_id;
char *thread_id;
char *filename;
@@ -33,7 +34,6 @@ struct _notmuch_message {
Xapian::Document doc;
};
-
/* "128 bits of thread-id ought to be enough for anybody" */
#define NOTMUCH_THREAD_ID_BITS 128
#define NOTMUCH_THREAD_ID_DIGITS (NOTMUCH_THREAD_ID_BITS / 4)
@@ -100,6 +100,8 @@ _notmuch_message_create (const void *talloc_owner,
message->notmuch = notmuch;
message->doc_id = doc_id;
+ message->frozen = 0;
+
/* Each of these will be lazily created as needed. */
message->message_id = NULL;
message->thread_id = NULL;
@@ -487,7 +489,8 @@ notmuch_message_add_tag (notmuch_message_t *message, const char *tag)
status);
}
- _notmuch_message_sync (message);
+ if (! message->frozen)
+ _notmuch_message_sync (message);
return NOTMUCH_STATUS_SUCCESS;
}
@@ -509,12 +512,56 @@ notmuch_message_remove_tag (notmuch_message_t *message, const char *tag)
status);
}
- _notmuch_message_sync (message);
+ if (! message->frozen)
+ _notmuch_message_sync (message);
return NOTMUCH_STATUS_SUCCESS;
}
void
+notmuch_message_remove_all_tags (notmuch_message_t *message)
+{
+ notmuch_private_status_t status;
+ notmuch_tags_t *tags;
+ const char *tag;
+
+ for (tags = notmuch_message_get_tags (message);
+ notmuch_tags_has_more (tags);
+ notmuch_tags_advance (tags))
+ {
+ tag = notmuch_tags_get (tags);
+
+ status = _notmuch_message_remove_term (message, "tag", tag);
+ if (status) {
+ INTERNAL_ERROR ("_notmuch_message_remove_term return unexpected value: %d\n",
+ status);
+ }
+ }
+
+ if (! message->frozen)
+ _notmuch_message_sync (message);
+}
+
+void
+notmuch_message_freeze (notmuch_message_t *message)
+{
+ message->frozen++;
+}
+
+notmuch_status_t
+notmuch_message_thaw (notmuch_message_t *message)
+{
+ if (message->frozen > 0) {
+ message->frozen--;
+ if (message->frozen == 0)
+ _notmuch_message_sync (message);
+ return NOTMUCH_STATUS_SUCCESS;
+ } else {
+ return NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW;
+ }
+}
+
+void
notmuch_message_destroy (notmuch_message_t *message)
{
talloc_free (message);
diff --git a/notmuch.c b/notmuch.c
index 3b0182a9..d716bce3 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -287,6 +287,7 @@ add_files_recursive (notmuch_database_t *notmuch,
case NOTMUCH_STATUS_FILE_ERROR:
case NOTMUCH_STATUS_NULL_POINTER:
case NOTMUCH_STATUS_TAG_TOO_LONG:
+ case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:
case NOTMUCH_STATUS_LAST_STATUS:
INTERNAL_ERROR ("add_message returned unexpected value: %d", status);
goto DONE;
@@ -854,6 +855,10 @@ restore_command (int argc, char *argv[])
goto NEXT_LINE;
}
+ notmuch_message_freeze (message);
+
+ notmuch_message_remove_all_tags (message);
+
next = tags;
while (next) {
tag = strsep (&next, " ");
@@ -869,6 +874,7 @@ restore_command (int argc, char *argv[])
}
}
+ notmuch_message_thaw (message);
notmuch_message_destroy (message);
}
NEXT_LINE:
diff --git a/notmuch.h b/notmuch.h
index 5e7d1240..7e83d239 100644
--- a/notmuch.h
+++ b/notmuch.h
@@ -75,6 +75,11 @@ typedef int notmuch_bool_t;
* NOTMUCH_STATUS_TAG_TOO_LONG: A tag value is too long (exceeds
* NOTMUCH_TAG_MAX)
*
+ * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: The notmuch_message_thaw
+ * function has been called more times than notmuch_message_freeze.
+ *
+ * And finally:
+ *
* NOTMUCH_STATUS_LAST_STATUS: Not an actual status value. Just a way
* to find out how many valid status values there are.
*/
@@ -87,6 +92,7 @@ typedef enum _notmuch_status {
NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID,
NOTMUCH_STATUS_NULL_POINTER,
NOTMUCH_STATUS_TAG_TOO_LONG,
+ NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW,
NOTMUCH_STATUS_LAST_STATUS
} notmuch_status_t;
@@ -650,8 +656,8 @@ notmuch_message_get_tags (notmuch_message_t *message);
*
* NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
*
- * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is longer than
- * too long (exceeds NOTMUCH_TAG_MAX)
+ * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
+ * (exceeds NOTMUCH_TAG_MAX)
*/
notmuch_status_t
notmuch_message_add_tag (notmuch_message_t *message, const char *tag);
@@ -660,16 +666,83 @@ notmuch_message_add_tag (notmuch_message_t *message, const char *tag);
*
* Return value:
*
- * NOTMUCH_STATUS_SUCCESS: Tag successfully added to message
+ * NOTMUCH_STATUS_SUCCESS: Tag successfully removed from message
*
* NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
*
- * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is longer than
- * too long (exceeds NOTMUCH_TAG_MAX)
+ * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
+ * (exceeds NOTMUCH_TAG_MAX)
*/
notmuch_status_t
notmuch_message_remove_tag (notmuch_message_t *message, const char *tag);
+/* Remove all tags from the given message.
+ *
+ * See notmuch_message_freeze for an example showing how to safely
+ * replace tag values.
+ */
+void
+notmuch_message_remove_all_tags (notmuch_message_t *message);
+
+/* Freeze the current state of 'message' within the database.
+ *
+ * This means that changes to the message state, (via
+ * notmuch_message_add_tag, notmuch_message_remove_tag, and
+ * notmuch_message_remove_all_tags), will not be committed to the
+ * database until the message is thawed with notmuch_message_thaw.
+ *
+ * Multiple calls to freeze/thaw are valid and these calls with
+ * "stack". That is there must be as many calls to thaw as to freeze
+ * before a message is actually thawed.
+ *
+ * The ability to do freeze/thaw allows for safe transactions to
+ * change tag values. For example, explicitly setting a message to
+ * have a given set of tags might look like this:
+ *
+ * notmuch_message_freeze (message);
+ *
+ * notmuch_message_remove_all_tags (message);
+ *
+ * for (i = 0; i < NUM_TAGS; i++)
+ * notmuch_message_add_tag (message, tags[i]);
+ *
+ * notmuch_message_thaw (message);
+ *
+ * With freeze/thaw used like this, the message in the database is
+ * guaranteed to have either the full set of original tag value, or
+ * the full set of new tag values, but nothing in between.
+ *
+ * Imagine the example above without freeze/thaw and the operation
+ * somehow getting interrupted. This could result in the message being
+ * left with no tags if the interruption happened after
+ * notmuch_message_remove_all_tags but before notmuch_message_add_tag.
+ */
+void
+notmuch_message_freeze (notmuch_message_t *message);
+
+/* Thaw the current 'message', synchronizing any changes that may have
+ * occurred while 'message' was frozen into the notmuch database.
+ *
+ * See notmuch_message_freeze for an example of how to use this
+ * function to safely provide tag changes.
+ *
+ * Multiple calls to freeze/thaw are valid and these calls with
+ * "stack". That is there must be as many calls to thaw as to freeze
+ * before a message is actually thawed.
+ *
+ * Return value:
+ *
+ * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least
+ * its frozen count has successfully been reduced by 1).
+ *
+ * NOTMUCH_STATUS_UNBALANCE_FREEZE_THAW: An attempt was made to thaw
+ * an unfrozen message. That is, there have been an unbalanced
+ * number of calls to notmuch_message_freeze and
+ * notmuch_message_thaw.
+ */
+notmuch_status_t
+notmuch_message_thaw (notmuch_message_t *message);
+
/* Destroy a notmuch_message_t object.
*
* It can be useful to call this function in the case of a single