mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-11-28 13:44:12 +01:00
notmuch restore: Fix to remove all tags before adding tags.
This means that the restore operation will now properly pick up the removal of tags indicated by the tag just not being present in the dump file. We added a few new public functions in order to support this: notmuch_message_freeze notmuch_message_remove_all_tags notmuch_message_thaw
This commit is contained in:
parent
9c4efa8487
commit
31db02a8c1
5 changed files with 136 additions and 15 deletions
7
TODO
7
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
|
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
|
encountered, (I noticed this is one thing that kept me from seeing the
|
||||||
same message-ID values as sup).
|
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.
|
|
||||||
|
|
|
@ -166,6 +166,8 @@ notmuch_status_to_string (notmuch_status_t status)
|
||||||
return "Erroneous NULL pointer";
|
return "Erroneous NULL pointer";
|
||||||
case NOTMUCH_STATUS_TAG_TOO_LONG:
|
case NOTMUCH_STATUS_TAG_TOO_LONG:
|
||||||
return "Tag value is too long (exceeds NOTMUCH_TAG_MAX)";
|
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:
|
default:
|
||||||
case NOTMUCH_STATUS_LAST_STATUS:
|
case NOTMUCH_STATUS_LAST_STATUS:
|
||||||
return "Unknown error status value";
|
return "Unknown error status value";
|
||||||
|
|
53
message.cc
53
message.cc
|
@ -26,6 +26,7 @@
|
||||||
struct _notmuch_message {
|
struct _notmuch_message {
|
||||||
notmuch_database_t *notmuch;
|
notmuch_database_t *notmuch;
|
||||||
Xapian::docid doc_id;
|
Xapian::docid doc_id;
|
||||||
|
int frozen;
|
||||||
char *message_id;
|
char *message_id;
|
||||||
char *thread_id;
|
char *thread_id;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
@ -33,7 +34,6 @@ struct _notmuch_message {
|
||||||
Xapian::Document doc;
|
Xapian::Document doc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* "128 bits of thread-id ought to be enough for anybody" */
|
/* "128 bits of thread-id ought to be enough for anybody" */
|
||||||
#define NOTMUCH_THREAD_ID_BITS 128
|
#define NOTMUCH_THREAD_ID_BITS 128
|
||||||
#define NOTMUCH_THREAD_ID_DIGITS (NOTMUCH_THREAD_ID_BITS / 4)
|
#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->notmuch = notmuch;
|
||||||
message->doc_id = doc_id;
|
message->doc_id = doc_id;
|
||||||
|
|
||||||
|
message->frozen = 0;
|
||||||
|
|
||||||
/* Each of these will be lazily created as needed. */
|
/* Each of these will be lazily created as needed. */
|
||||||
message->message_id = NULL;
|
message->message_id = NULL;
|
||||||
message->thread_id = NULL;
|
message->thread_id = NULL;
|
||||||
|
@ -487,7 +489,8 @@ notmuch_message_add_tag (notmuch_message_t *message, const char *tag)
|
||||||
status);
|
status);
|
||||||
}
|
}
|
||||||
|
|
||||||
_notmuch_message_sync (message);
|
if (! message->frozen)
|
||||||
|
_notmuch_message_sync (message);
|
||||||
|
|
||||||
return NOTMUCH_STATUS_SUCCESS;
|
return NOTMUCH_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -509,11 +512,55 @@ notmuch_message_remove_tag (notmuch_message_t *message, const char *tag)
|
||||||
status);
|
status);
|
||||||
}
|
}
|
||||||
|
|
||||||
_notmuch_message_sync (message);
|
if (! message->frozen)
|
||||||
|
_notmuch_message_sync (message);
|
||||||
|
|
||||||
return NOTMUCH_STATUS_SUCCESS;
|
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
|
void
|
||||||
notmuch_message_destroy (notmuch_message_t *message)
|
notmuch_message_destroy (notmuch_message_t *message)
|
||||||
{
|
{
|
||||||
|
|
|
@ -287,6 +287,7 @@ add_files_recursive (notmuch_database_t *notmuch,
|
||||||
case NOTMUCH_STATUS_FILE_ERROR:
|
case NOTMUCH_STATUS_FILE_ERROR:
|
||||||
case NOTMUCH_STATUS_NULL_POINTER:
|
case NOTMUCH_STATUS_NULL_POINTER:
|
||||||
case NOTMUCH_STATUS_TAG_TOO_LONG:
|
case NOTMUCH_STATUS_TAG_TOO_LONG:
|
||||||
|
case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:
|
||||||
case NOTMUCH_STATUS_LAST_STATUS:
|
case NOTMUCH_STATUS_LAST_STATUS:
|
||||||
INTERNAL_ERROR ("add_message returned unexpected value: %d", status);
|
INTERNAL_ERROR ("add_message returned unexpected value: %d", status);
|
||||||
goto DONE;
|
goto DONE;
|
||||||
|
@ -854,6 +855,10 @@ restore_command (int argc, char *argv[])
|
||||||
goto NEXT_LINE;
|
goto NEXT_LINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notmuch_message_freeze (message);
|
||||||
|
|
||||||
|
notmuch_message_remove_all_tags (message);
|
||||||
|
|
||||||
next = tags;
|
next = tags;
|
||||||
while (next) {
|
while (next) {
|
||||||
tag = strsep (&next, " ");
|
tag = strsep (&next, " ");
|
||||||
|
@ -869,6 +874,7 @@ restore_command (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notmuch_message_thaw (message);
|
||||||
notmuch_message_destroy (message);
|
notmuch_message_destroy (message);
|
||||||
}
|
}
|
||||||
NEXT_LINE:
|
NEXT_LINE:
|
||||||
|
|
83
notmuch.h
83
notmuch.h
|
@ -75,6 +75,11 @@ typedef int notmuch_bool_t;
|
||||||
* NOTMUCH_STATUS_TAG_TOO_LONG: A tag value is too long (exceeds
|
* NOTMUCH_STATUS_TAG_TOO_LONG: A tag value is too long (exceeds
|
||||||
* NOTMUCH_TAG_MAX)
|
* 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
|
* NOTMUCH_STATUS_LAST_STATUS: Not an actual status value. Just a way
|
||||||
* to find out how many valid status values there are.
|
* 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_DUPLICATE_MESSAGE_ID,
|
||||||
NOTMUCH_STATUS_NULL_POINTER,
|
NOTMUCH_STATUS_NULL_POINTER,
|
||||||
NOTMUCH_STATUS_TAG_TOO_LONG,
|
NOTMUCH_STATUS_TAG_TOO_LONG,
|
||||||
|
NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW,
|
||||||
|
|
||||||
NOTMUCH_STATUS_LAST_STATUS
|
NOTMUCH_STATUS_LAST_STATUS
|
||||||
} notmuch_status_t;
|
} 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_NULL_POINTER: The 'tag' argument is NULL
|
||||||
*
|
*
|
||||||
* NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is longer than
|
* NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
|
||||||
* too long (exceeds NOTMUCH_TAG_MAX)
|
* (exceeds NOTMUCH_TAG_MAX)
|
||||||
*/
|
*/
|
||||||
notmuch_status_t
|
notmuch_status_t
|
||||||
notmuch_message_add_tag (notmuch_message_t *message, const char *tag);
|
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:
|
* 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_NULL_POINTER: The 'tag' argument is NULL
|
||||||
*
|
*
|
||||||
* NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is longer than
|
* NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
|
||||||
* too long (exceeds NOTMUCH_TAG_MAX)
|
* (exceeds NOTMUCH_TAG_MAX)
|
||||||
*/
|
*/
|
||||||
notmuch_status_t
|
notmuch_status_t
|
||||||
notmuch_message_remove_tag (notmuch_message_t *message, const char *tag);
|
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.
|
/* Destroy a notmuch_message_t object.
|
||||||
*
|
*
|
||||||
* It can be useful to call this function in the case of a single
|
* It can be useful to call this function in the case of a single
|
||||||
|
|
Loading…
Reference in a new issue