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:
Carl Worth 2009-10-26 22:25:45 -07:00
parent 9c4efa8487
commit 31db02a8c1
5 changed files with 136 additions and 15 deletions

7
TODO
View file

@ -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.

View file

@ -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";

View file

@ -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)
{ {

View file

@ -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:

View file

@ -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