1 Return-Path: <amthrax@drake.mit.edu>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id 28D41429E4A
\r
6 for <notmuch@notmuchmail.org>; Thu, 17 Feb 2011 23:59:38 -0800 (PST)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5
\r
12 tests=[RCVD_IN_DNSWL_NONE=-0.0001] autolearn=disabled
\r
13 Received: from olra.theworths.org ([127.0.0.1])
\r
14 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
15 with ESMTP id ZT6zN79RrO8j for <notmuch@notmuchmail.org>;
\r
16 Thu, 17 Feb 2011 23:59:36 -0800 (PST)
\r
17 Received: from dmz-mailsec-scanner-7.mit.edu (DMZ-MAILSEC-SCANNER-7.MIT.EDU
\r
19 by olra.theworths.org (Postfix) with ESMTP id 33F61429E38
\r
20 for <notmuch@notmuchmail.org>; Thu, 17 Feb 2011 23:59:29 -0800 (PST)
\r
21 X-AuditID: 12074424-b7b0bae000000a05-6d-4d5e26e04bad
\r
22 Received: from mailhub-auth-3.mit.edu ( [18.9.21.43])
\r
23 by dmz-mailsec-scanner-7.mit.edu (Symantec Brightmail Gateway) with
\r
24 SMTP id 8D.70.02565.0E62E5D4; Fri, 18 Feb 2011 02:59:28 -0500 (EST)
\r
25 Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])
\r
26 by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id p1I7xRwj029792;
\r
27 Fri, 18 Feb 2011 02:59:27 -0500
\r
28 Received: from drake.mit.edu
\r
29 (209-6-116-242.c3-0.arl-ubr1.sbo-arl.ma.cable.rcn.com
\r
30 [209.6.116.242]) (authenticated bits=0)
\r
31 (User authenticated as amdragon@ATHENA.MIT.EDU)
\r
32 by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id p1I7xQp1001544
\r
33 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);
\r
34 Fri, 18 Feb 2011 02:59:27 -0500 (EST)
\r
35 Received: from amthrax by drake.mit.edu with local (Exim 4.72)
\r
36 (envelope-from <amthrax@drake.mit.edu>)
\r
37 id 1PqLFO-0008LK-Jg; Fri, 18 Feb 2011 02:59:26 -0500
\r
38 From: Austin Clements <amdragon@MIT.EDU>
\r
39 To: notmuch@notmuchmail.org
\r
40 Subject: [PATCH 06/10] lib: Add API's to find by filename and remove a
\r
41 filename from a message.
\r
42 Date: Fri, 18 Feb 2011 02:58:56 -0500
\r
43 Message-Id: <1298015940-31986-7-git-send-email-amdragon@mit.edu>
\r
44 X-Mailer: git-send-email 1.7.2.3
\r
45 In-Reply-To: <1298015940-31986-1-git-send-email-amdragon@mit.edu>
\r
46 References: <1298015940-31986-1-git-send-email-amdragon@mit.edu>
\r
47 X-Brightmail-Tracker: AAAAAA==
\r
48 Cc: amdragon@mit.edu
\r
49 X-BeenThere: notmuch@notmuchmail.org
\r
50 X-Mailman-Version: 2.1.13
\r
52 List-Id: "Use and development of the notmuch mail system."
\r
53 <notmuch.notmuchmail.org>
\r
54 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
55 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
56 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
57 List-Post: <mailto:notmuch@notmuchmail.org>
\r
58 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
59 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
60 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
61 X-List-Received-Date: Fri, 18 Feb 2011 07:59:38 -0000
\r
63 The two new API functions, notmuch_database_find_message_by_filename
\r
64 and notmuch_message_remove_filename give library users more control
\r
65 over the filename removal process. notmuch_database_remove_message
\r
66 has been reimplemented in terms of these new functions.
\r
68 notmuch_message_remove_filename acts much like
\r
69 notmuch_message_remove_tag in that it does not synchronize with the
\r
70 database if the message is frozen. Thus, callers can freeze a message
\r
71 to remove a filename and perform tag synchronization in one atomic
\r
74 This new approach also naturally eliminates an atomicity violation in
\r
75 the old code. Previously, notmuch_database_remove_message would first
\r
76 update the database document to remove the filename and only then
\r
77 remove the document if it had no more filenames left. An interruption
\r
78 between these two steps resulted in a permanently un-removable zombie
\r
79 message that would produce errors and odd results in searches. Since
\r
80 this new approach delegates document deletion to
\r
81 _notmuch_message_sync, the document will be deleted without first
\r
82 being updated, eliminating this window.
\r
84 lib/database.cc | 58 +++++++++++++++++++-----------------------------------
\r
85 lib/message.cc | 21 +++++++++++++++++++
\r
86 lib/notmuch.h | 43 ++++++++++++++++++++++++++++++++++++++-
\r
87 3 files changed, 83 insertions(+), 39 deletions(-)
\r
89 diff --git a/lib/database.cc b/lib/database.cc
\r
90 index d88b168..bee1e96 100644
\r
91 --- a/lib/database.cc
\r
92 +++ b/lib/database.cc
\r
93 @@ -1689,72 +1689,56 @@ notmuch_status_t
\r
94 notmuch_database_remove_message (notmuch_database_t *notmuch,
\r
95 const char *filename)
\r
97 - Xapian::WritableDatabase *db;
\r
98 + notmuch_message_t *message;
\r
99 + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
\r
100 + message = notmuch_database_find_message_by_filename (notmuch, filename);
\r
102 + status = notmuch_message_remove_filename (message, filename);
\r
103 + notmuch_message_destroy (message);
\r
108 +notmuch_message_t *
\r
109 +notmuch_database_find_message_by_filename (notmuch_database_t *notmuch,
\r
110 + const char *filename)
\r
113 const char *prefix = _find_prefix ("file-direntry");
\r
114 char *direntry, *term;
\r
115 Xapian::PostingIterator i, end;
\r
116 - Xapian::Document document;
\r
117 + notmuch_message_t *message = NULL;
\r
118 notmuch_status_t status;
\r
120 - status = _notmuch_database_ensure_writable (notmuch);
\r
124 local = talloc_new (notmuch);
\r
126 - db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
\r
130 status = _notmuch_database_filename_to_direntry (local, notmuch,
\r
131 filename, &direntry);
\r
136 term = talloc_asprintf (local, "%s%s", prefix, direntry);
\r
138 find_doc_ids_for_term (notmuch, term, &i, &end);
\r
140 - for ( ; i != end; i++) {
\r
141 - Xapian::TermIterator j;
\r
142 - notmuch_message_t *message;
\r
144 notmuch_private_status_t private_status;
\r
146 - message = _notmuch_message_create (local, notmuch,
\r
147 + message = _notmuch_message_create (notmuch, notmuch,
\r
148 *i, &private_status);
\r
149 - if (message == NULL)
\r
150 - return COERCE_STATUS (private_status,
\r
151 - "Inconsistent document ID in datbase.");
\r
153 - _notmuch_message_remove_filename (message, filename);
\r
154 - _notmuch_message_sync (message);
\r
156 - /* Take care to find document after sync'ing filename removal. */
\r
157 - document = find_document_for_doc_id (notmuch, *i);
\r
158 - j = document.termlist_begin ();
\r
159 - j.skip_to (prefix);
\r
161 - /* Was this the last file-direntry in the message? */
\r
162 - if (j == document.termlist_end () ||
\r
163 - strncmp ((*j).c_str (), prefix, strlen (prefix)))
\r
165 - db->delete_document (document.get_docid ());
\r
166 - status = NOTMUCH_STATUS_SUCCESS;
\r
168 - status = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;
\r
171 } catch (const Xapian::Error &error) {
\r
172 - fprintf (stderr, "Error: A Xapian exception occurred removing message: %s\n",
\r
173 + fprintf (stderr, "Error: A Xapian exception occurred finding message by filename: %s\n",
\r
174 error.get_msg().c_str());
\r
175 notmuch->exception_reported = TRUE;
\r
176 - status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
\r
180 talloc_free (local);
\r
187 diff --git a/lib/message.cc b/lib/message.cc
\r
188 index 635f5cf..b4adb5c 100644
\r
189 --- a/lib/message.cc
\r
190 +++ b/lib/message.cc
\r
191 @@ -1300,6 +1300,27 @@ notmuch_message_remove_all_tags (notmuch_message_t *message)
\r
195 +notmuch_message_remove_filename (notmuch_message_t *message,
\r
196 + const char *filename)
\r
198 + notmuch_status_t status;
\r
200 + status = _notmuch_database_ensure_writable (message->notmuch);
\r
204 + status = _notmuch_message_remove_filename (message, filename);
\r
205 + /* Was this the last file-direntry in the message? */
\r
206 + if (status == NOTMUCH_STATUS_SUCCESS)
\r
207 + message->deleted = TRUE;
\r
209 + if (! message->frozen)
\r
210 + _notmuch_message_sync (message);
\r
216 notmuch_message_freeze (notmuch_message_t *message)
\r
218 notmuch_status_t status;
\r
219 diff --git a/lib/notmuch.h b/lib/notmuch.h
\r
220 index e508309..61030cb 100644
\r
221 --- a/lib/notmuch.h
\r
222 +++ b/lib/notmuch.h
\r
223 @@ -316,6 +316,22 @@ notmuch_message_t *
\r
224 notmuch_database_find_message (notmuch_database_t *database,
\r
225 const char *message_id);
\r
227 +/* Find a message with the given filename.
\r
229 + * If the database contains a message with the given filename, then a
\r
230 + * new notmuch_message_t object is returned. The caller should call
\r
231 + * notmuch_message_destroy when done with the message.
\r
233 + * This function returns NULL in the following situations:
\r
235 + * * No message is found with the given filename
\r
236 + * * An out-of-memory situation occurs
\r
237 + * * A Xapian exception occurs
\r
239 +notmuch_message_t *
\r
240 +notmuch_database_find_message_by_filename (notmuch_database_t *notmuch,
\r
241 + const char *filename);
\r
243 /* Return a list of all tags found in the database.
\r
245 * This function creates a list of all tags found in the database. The
\r
246 @@ -979,11 +995,34 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message);
\r
248 notmuch_message_tags_to_maildir_flags (notmuch_message_t *message);
\r
250 +/* Remove a filename from a message. If this is the last copy of this
\r
251 + * message, also delete it from the database.
\r
253 + * Much like notmuch_message_remove_tag, if message is frozen, it will
\r
254 + * not be removed from or updated in the database until thawed.
\r
258 + * NOTMUCH_STATUS_SUCCESS: The last filename was removed and the
\r
259 + * message was removed from the database.
\r
261 + * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: This filename was removed but
\r
262 + * the message persists in the database with at least one other
\r
265 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
266 + * mode so no message can be removed.
\r
269 +notmuch_message_remove_filename (notmuch_message_t *message,
\r
270 + const char *filename);
\r
272 /* Freeze the current state of 'message' within the database.
\r
274 * This means that changes to the message state, (via
\r
275 - * notmuch_message_add_tag, notmuch_message_remove_tag, and
\r
276 - * notmuch_message_remove_all_tags), will not be committed to the
\r
277 + * notmuch_message_add_tag, notmuch_message_remove_tag,
\r
278 + * notmuch_message_remove_all_tags, and
\r
279 + * notmuch_message_remove_filename), will not be committed to the
\r
280 * database until the message is thawed with notmuch_message_thaw.
\r
282 * Multiple calls to freeze/thaw are valid and these calls will
\r