[PATCH 06/10] lib: Add API's to find by filename and remove a filename from a message.
authorAustin Clements <amdragon@MIT.EDU>
Fri, 18 Feb 2011 07:58:56 +0000 (02:58 +1900)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:37:55 +0000 (09:37 -0800)
b7/edf951c27d141015c04bb407d04034635493ef [new file with mode: 0644]

diff --git a/b7/edf951c27d141015c04bb407d04034635493ef b/b7/edf951c27d141015c04bb407d04034635493ef
new file mode 100644 (file)
index 0000000..51c1de3
--- /dev/null
@@ -0,0 +1,285 @@
+Return-Path: <amthrax@drake.mit.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id 28D41429E4A\r
+       for <notmuch@notmuchmail.org>; Thu, 17 Feb 2011 23:59:38 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5\r
+       tests=[RCVD_IN_DNSWL_NONE=-0.0001] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id ZT6zN79RrO8j for <notmuch@notmuchmail.org>;\r
+       Thu, 17 Feb 2011 23:59:36 -0800 (PST)\r
+Received: from dmz-mailsec-scanner-7.mit.edu (DMZ-MAILSEC-SCANNER-7.MIT.EDU\r
+       [18.7.68.36])\r
+       by olra.theworths.org (Postfix) with ESMTP id 33F61429E38\r
+       for <notmuch@notmuchmail.org>; Thu, 17 Feb 2011 23:59:29 -0800 (PST)\r
+X-AuditID: 12074424-b7b0bae000000a05-6d-4d5e26e04bad\r
+Received: from mailhub-auth-3.mit.edu ( [18.9.21.43])\r
+       by dmz-mailsec-scanner-7.mit.edu (Symantec Brightmail Gateway) with\r
+       SMTP id 8D.70.02565.0E62E5D4; Fri, 18 Feb 2011 02:59:28 -0500 (EST)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+       by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id p1I7xRwj029792; \r
+       Fri, 18 Feb 2011 02:59:27 -0500\r
+Received: from drake.mit.edu\r
+       (209-6-116-242.c3-0.arl-ubr1.sbo-arl.ma.cable.rcn.com\r
+       [209.6.116.242]) (authenticated bits=0)\r
+       (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+       by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id p1I7xQp1001544\r
+       (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+       Fri, 18 Feb 2011 02:59:27 -0500 (EST)\r
+Received: from amthrax by drake.mit.edu with local (Exim 4.72)\r
+       (envelope-from <amthrax@drake.mit.edu>)\r
+       id 1PqLFO-0008LK-Jg; Fri, 18 Feb 2011 02:59:26 -0500\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH 06/10] lib: Add API's to find by filename and remove a\r
+       filename from a message.\r
+Date: Fri, 18 Feb 2011 02:58:56 -0500\r
+Message-Id: <1298015940-31986-7-git-send-email-amdragon@mit.edu>\r
+X-Mailer: git-send-email 1.7.2.3\r
+In-Reply-To: <1298015940-31986-1-git-send-email-amdragon@mit.edu>\r
+References: <1298015940-31986-1-git-send-email-amdragon@mit.edu>\r
+X-Brightmail-Tracker: AAAAAA==\r
+Cc: amdragon@mit.edu\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Fri, 18 Feb 2011 07:59:38 -0000\r
+\r
+The two new API functions, notmuch_database_find_message_by_filename\r
+and notmuch_message_remove_filename give library users more control\r
+over the filename removal process.  notmuch_database_remove_message\r
+has been reimplemented in terms of these new functions.\r
+\r
+notmuch_message_remove_filename acts much like\r
+notmuch_message_remove_tag in that it does not synchronize with the\r
+database if the message is frozen.  Thus, callers can freeze a message\r
+to remove a filename and perform tag synchronization in one atomic\r
+operation.\r
+\r
+This new approach also naturally eliminates an atomicity violation in\r
+the old code.  Previously, notmuch_database_remove_message would first\r
+update the database document to remove the filename and only then\r
+remove the document if it had no more filenames left.  An interruption\r
+between these two steps resulted in a permanently un-removable zombie\r
+message that would produce errors and odd results in searches.  Since\r
+this new approach delegates document deletion to\r
+_notmuch_message_sync, the document will be deleted without first\r
+being updated, eliminating this window.\r
+---\r
+ lib/database.cc |   58 +++++++++++++++++++-----------------------------------\r
+ lib/message.cc  |   21 +++++++++++++++++++\r
+ lib/notmuch.h   |   43 ++++++++++++++++++++++++++++++++++++++-\r
+ 3 files changed, 83 insertions(+), 39 deletions(-)\r
+\r
+diff --git a/lib/database.cc b/lib/database.cc\r
+index d88b168..bee1e96 100644\r
+--- a/lib/database.cc\r
++++ b/lib/database.cc\r
+@@ -1689,72 +1689,56 @@ notmuch_status_t\r
+ notmuch_database_remove_message (notmuch_database_t *notmuch,\r
+                                const char *filename)\r
+ {\r
+-    Xapian::WritableDatabase *db;\r
++    notmuch_message_t *message;\r
++    notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;\r
++    message = notmuch_database_find_message_by_filename (notmuch, filename);\r
++    if (message) {\r
++      status = notmuch_message_remove_filename (message, filename);\r
++      notmuch_message_destroy (message);\r
++    }\r
++    return status;\r
++}\r
++\r
++notmuch_message_t *\r
++notmuch_database_find_message_by_filename (notmuch_database_t *notmuch,\r
++                                         const char *filename)\r
++{\r
+     void *local;\r
+     const char *prefix = _find_prefix ("file-direntry");\r
+     char *direntry, *term;\r
+     Xapian::PostingIterator i, end;\r
+-    Xapian::Document document;\r
++    notmuch_message_t *message = NULL;\r
+     notmuch_status_t status;\r
\r
+-    status = _notmuch_database_ensure_writable (notmuch);\r
+-    if (status)\r
+-      return status;\r
+-\r
+     local = talloc_new (notmuch);\r
\r
+-    db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);\r
+-\r
+     try {\r
\r
+       status = _notmuch_database_filename_to_direntry (local, notmuch,\r
+                                                        filename, &direntry);\r
+       if (status)\r
+-          return status;\r
++          return NULL;\r
\r
+       term = talloc_asprintf (local, "%s%s", prefix, direntry);\r
\r
+       find_doc_ids_for_term (notmuch, term, &i, &end);\r
\r
+-      for ( ; i != end; i++) {\r
+-          Xapian::TermIterator j;\r
+-          notmuch_message_t *message;\r
++      if (i != end) {\r
+           notmuch_private_status_t private_status;\r
\r
+-          message = _notmuch_message_create (local, notmuch,\r
++          message = _notmuch_message_create (notmuch, notmuch,\r
+                                              *i, &private_status);\r
+-          if (message == NULL)\r
+-              return COERCE_STATUS (private_status,\r
+-                                    "Inconsistent document ID in datbase.");\r
+-\r
+-          _notmuch_message_remove_filename (message, filename);\r
+-          _notmuch_message_sync (message);\r
+-\r
+-          /* Take care to find document after sync'ing filename removal. */\r
+-          document = find_document_for_doc_id (notmuch, *i);\r
+-          j = document.termlist_begin ();\r
+-          j.skip_to (prefix);\r
+-\r
+-          /* Was this the last file-direntry in the message? */\r
+-          if (j == document.termlist_end () ||\r
+-              strncmp ((*j).c_str (), prefix, strlen (prefix)))\r
+-          {\r
+-              db->delete_document (document.get_docid ());\r
+-              status = NOTMUCH_STATUS_SUCCESS;\r
+-          } else {\r
+-              status = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;\r
+-          }\r
+       }\r
+     } catch (const Xapian::Error &error) {\r
+-      fprintf (stderr, "Error: A Xapian exception occurred removing message: %s\n",\r
++      fprintf (stderr, "Error: A Xapian exception occurred finding message by filename: %s\n",\r
+                error.get_msg().c_str());\r
+       notmuch->exception_reported = TRUE;\r
+-      status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;\r
++      message = NULL;\r
+     }\r
\r
+     talloc_free (local);\r
\r
+-    return status;\r
++    return message;\r
+ }\r
\r
+ notmuch_tags_t *\r
+diff --git a/lib/message.cc b/lib/message.cc\r
+index 635f5cf..b4adb5c 100644\r
+--- a/lib/message.cc\r
++++ b/lib/message.cc\r
+@@ -1300,6 +1300,27 @@ notmuch_message_remove_all_tags (notmuch_message_t *message)\r
+ }\r
\r
+ notmuch_status_t\r
++notmuch_message_remove_filename (notmuch_message_t *message,\r
++                               const char *filename)\r
++{\r
++    notmuch_status_t status;\r
++\r
++    status = _notmuch_database_ensure_writable (message->notmuch);\r
++    if (status)\r
++      return status;\r
++\r
++    status = _notmuch_message_remove_filename (message, filename);\r
++    /* Was this the last file-direntry in the message? */\r
++    if (status == NOTMUCH_STATUS_SUCCESS)\r
++      message->deleted = TRUE;\r
++\r
++    if (! message->frozen)\r
++      _notmuch_message_sync (message);\r
++\r
++    return status;\r
++}\r
++\r
++notmuch_status_t\r
+ notmuch_message_freeze (notmuch_message_t *message)\r
+ {\r
+     notmuch_status_t status;\r
+diff --git a/lib/notmuch.h b/lib/notmuch.h\r
+index e508309..61030cb 100644\r
+--- a/lib/notmuch.h\r
++++ b/lib/notmuch.h\r
+@@ -316,6 +316,22 @@ notmuch_message_t *\r
+ notmuch_database_find_message (notmuch_database_t *database,\r
+                              const char *message_id);\r
\r
++/* Find a message with the given filename.\r
++ *\r
++ * If the database contains a message with the given filename, then a\r
++ * new notmuch_message_t object is returned.  The caller should call \r
++ * notmuch_message_destroy when done with the message.\r
++ *\r
++ * This function returns NULL in the following situations:\r
++ *\r
++ *    * No message is found with the given filename\r
++ *    * An out-of-memory situation occurs\r
++ *    * A Xapian exception occurs\r
++ */\r
++notmuch_message_t *\r
++notmuch_database_find_message_by_filename (notmuch_database_t *notmuch,\r
++                                         const char *filename);\r
++\r
+ /* Return a list of all tags found in the database.\r
+  *\r
+  * This function creates a list of all tags found in the database. The\r
+@@ -979,11 +995,34 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message);\r
+ notmuch_status_t\r
+ notmuch_message_tags_to_maildir_flags (notmuch_message_t *message);\r
\r
++/* Remove a filename from a message.  If this is the last copy of this\r
++ * message, also delete it from the database.\r
++ *\r
++ * Much like notmuch_message_remove_tag, if message is frozen, it will\r
++ * not be removed from or updated in the database until thawed.\r
++ *\r
++ * Return value:\r
++ *\r
++ * NOTMUCH_STATUS_SUCCESS: The last filename was removed and the\r
++ *    message was removed from the database.\r
++ *\r
++ * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: This filename was removed but\r
++ *    the message persists in the database with at least one other\r
++ *    filename.\r
++ *\r
++ * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
++ *    mode so no message can be removed.\r
++ */\r
++notmuch_status_t\r
++notmuch_message_remove_filename (notmuch_message_t *message,\r
++                               const char *filename);\r
++\r
+ /* Freeze the current state of 'message' within the database.\r
+  *\r
+  * This means that changes to the message state, (via\r
+- * notmuch_message_add_tag, notmuch_message_remove_tag, and\r
+- * notmuch_message_remove_all_tags), will not be committed to the\r
++ * notmuch_message_add_tag, notmuch_message_remove_tag,\r
++ * notmuch_message_remove_all_tags, and\r
++ * notmuch_message_remove_filename), will not be committed to the\r
+  * database until the message is thawed with notmuch_message_thaw.\r
+  *\r
+  * Multiple calls to freeze/thaw are valid and these calls will\r
+-- \r
+1.7.2.3\r
+\r