Re: [PATCH] emacs: wash: make word-wrap bound message width
[notmuch-archives.git] / 4f / 394bd5eb18e7a418757e10667eeb959829e4ca
1 Return-Path: <amdragon@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 88971431FAE\r
6         for <notmuch@notmuchmail.org>; Thu, 31 Jul 2014 19:11:07 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -2.3\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-2.3 tagged_above=-999 required=5\r
12         tests=[RCVD_IN_DNSWL_MED=-2.3] 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 p53+8YjIV53W for <notmuch@notmuchmail.org>;\r
16         Thu, 31 Jul 2014 19:11:00 -0700 (PDT)\r
17 Received: from dmz-mailsec-scanner-2.mit.edu (dmz-mailsec-scanner-2.mit.edu\r
18         [18.9.25.13])\r
19         (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
20         (No client certificate requested)\r
21         by olra.theworths.org (Postfix) with ESMTPS id 94C1E431FD8\r
22         for <notmuch@notmuchmail.org>; Thu, 31 Jul 2014 19:10:21 -0700 (PDT)\r
23 X-AuditID: 1209190d-f79c06d000002f07-fd-53daf70c3615\r
24 Received: from mailhub-auth-4.mit.edu ( [18.7.62.39])\r
25         (using TLS with cipher AES256-SHA (256/256 bits))\r
26         (Client did not present a certificate)\r
27         by dmz-mailsec-scanner-2.mit.edu (Symantec Messaging Gateway) with SMTP\r
28         id C7.27.12039.C07FAD35; Thu, 31 Jul 2014 22:10:20 -0400 (EDT)\r
29 Received: from outgoing.mit.edu (outgoing-auth-1.mit.edu [18.9.28.11])\r
30         by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id s712AABo010402; \r
31         Thu, 31 Jul 2014 22:10:10 -0400\r
32 Received: from drake.dyndns.org\r
33         (216-15-114-40.c3-0.arl-ubr1.sbo-arl.ma.cable.rcn.com\r
34         [216.15.114.40]) (authenticated bits=0)\r
35         (User authenticated as amdragon@ATHENA.MIT.EDU)\r
36         by outgoing.mit.edu (8.13.8/8.12.4) with ESMTP id s712A6pp029977\r
37         (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
38         Thu, 31 Jul 2014 22:10:08 -0400\r
39 Received: from amthrax by drake.dyndns.org with local (Exim 4.77)\r
40         (envelope-from <amdragon@mit.edu>)\r
41         id 1XD2IH-00033c-UP; Thu, 31 Jul 2014 22:10:05 -0400\r
42 From: Austin Clements <amdragon@MIT.EDU>\r
43 To: notmuch@notmuchmail.org\r
44 Subject: [PATCH v3 04/13] lib: Database version 3: Introduce fine-grained\r
45         "features"\r
46 Date: Thu, 31 Jul 2014 22:09:54 -0400\r
47 Message-Id: <1406859003-11561-5-git-send-email-amdragon@mit.edu>\r
48 X-Mailer: git-send-email 2.0.0\r
49 In-Reply-To: <1406859003-11561-1-git-send-email-amdragon@mit.edu>\r
50 References: <1406859003-11561-1-git-send-email-amdragon@mit.edu>\r
51 X-Brightmail-Tracker:\r
52  H4sIAAAAAAAAA+NgFtrLIsWRmVeSWpSXmKPExsUixG6nrsvz/VawQWebucXquTwW12/OZHZg\r
53         8tg56y67x7NVt5gDmKK4bFJSczLLUov07RK4Mr4/n8JYsKqmYv2yGawNjFsSuhg5OSQETCRW\r
54         7X7NBmGLSVy4tx7I5uIQEpjNJHFvdQsjhLORUWL/47/sEM4dJokTxy+xQDhzGSV2HdjACtLP\r
55         JqAhsW3/ckYQW0RAWmLn3dlgcWYBR4nP+xeB7RAWCJF4fOI0C4jNIqAqMWPdFzCbV8BB4uX6\r
56         nYwQd8hJNNz4BFbPCdT7/98CsDlCQDWtp7cyT2DkX8DIsIpRNiW3Sjc3MTOnODVZtzg5MS8v\r
57         tUjXSC83s0QvNaV0EyMolDgleXcwvjuodIhRgINRiYfXIfRWsBBrYllxZe4hRkkOJiVRXpuP\r
58         QCG+pPyUyozE4oz4otKc1OJDjBIczEoivC8/AOV4UxIrq1KL8mFS0hwsSuK8b62tgoUE0hNL\r
59         UrNTUwtSi2CyMhwcShK8Z74CNQoWpaanVqRl5pQgpJk4OEGG8wANvwZSw1tckJhbnJkOkT/F\r
60         qCglzrsHJCEAksgozYPrhcX6K0ZxoFeEeZeCVPEA0wRc9yugwUxAg5/fug4yuCQRISXVwFh8\r
61         Qej4mqaeq4/UO+ctL3z5bLV56ba07/732HydLj/5Fb3t/OJ5S6avfXx5Xc+1+ps7HynMrXo6\r
62         08DmD5uFdrLIU1kmdR/ZruyzVwNtl3YwqrzsuLHkwbln7jfrDFXet83L7orLen3KV2273/qr\r
63         9QuuyGk0/Jwzl4Oz9vyb/vDnyxbomhxZZSKoxFKckWioxVxUnAgAew3l2tACAAA=\r
64 X-BeenThere: notmuch@notmuchmail.org\r
65 X-Mailman-Version: 2.1.13\r
66 Precedence: list\r
67 List-Id: "Use and development of the notmuch mail system."\r
68         <notmuch.notmuchmail.org>\r
69 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
70         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
71 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
72 List-Post: <mailto:notmuch@notmuchmail.org>\r
73 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
74 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
75         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
76 X-List-Received-Date: Fri, 01 Aug 2014 02:11:07 -0000\r
77 \r
78 Previously, our database schema was versioned by a single number.\r
79 Each database schema change had to occur "atomically" in Notmuch's\r
80 development history: before some commit, Notmuch used version N, after\r
81 that commit, it used version N+1.  Hence, each new schema version\r
82 could introduce only one change, the task of developing a schema\r
83 change fell on a single person, and it all had to happen and be\r
84 perfect in a single commit series.  This made introducing a new schema\r
85 version hard.  We've seen only two schema changes in the history of\r
86 Notmuch.\r
87 \r
88 This commit introduces database schema version 3; hopefully the last\r
89 schema version we'll need for a while.  With this version, we switch\r
90 from a single version number to "features": a set of named,\r
91 independent aspects of the database schema.\r
92 \r
93 Features should make backwards compatibility easier.  For many things,\r
94 it should be easy to support databases both with and without a\r
95 feature, which will allow us to make upgrades optional and will enable\r
96 "unstable" features that can be developed and tested over time.\r
97 \r
98 Features also make forwards compatibility easier.  The features\r
99 recorded in a database include "compatibility flags," which can\r
100 indicate to an older version of Notmuch when it must support a given\r
101 feature to open the database for read or for write.  This lets us\r
102 replace the old vague "I don't recognize this version, so something\r
103 might go wrong, but I promise to try my best" warnings upon opening a\r
104 database with an unknown version with precise errors.  If a database\r
105 is safe to open for read/write despite unknown features, an older\r
106 version will know that and issue no message at all.  If the database\r
107 is not safe to open for read/write because of unknown features, an\r
108 older version will know that, too, and can tell the user exactly which\r
109 required features it lacks support for.\r
110 ---\r
111  lib/database-private.h |  57 ++++++++++++++-\r
112  lib/database.cc        | 190 ++++++++++++++++++++++++++++++++++++++++---------\r
113  2 files changed, 213 insertions(+), 34 deletions(-)\r
114 \r
115 diff --git a/lib/database-private.h b/lib/database-private.h\r
116 index d3e65fd..2ffab33 100644\r
117 --- a/lib/database-private.h\r
118 +++ b/lib/database-private.h\r
119 @@ -41,11 +41,15 @@ struct _notmuch_database {\r
120  \r
121      char *path;\r
122  \r
123 -    notmuch_bool_t needs_upgrade;\r
124      notmuch_database_mode_t mode;\r
125      int atomic_nesting;\r
126      Xapian::Database *xapian_db;\r
127  \r
128 +    /* Bit mask of features used by this database.  Features are\r
129 +     * named, independent aspects of the database schema.  This is a\r
130 +     * bitwise-OR of NOTMUCH_FEATURE_* values (below). */\r
131 +    unsigned int features;\r
132 +\r
133      unsigned int last_doc_id;\r
134      uint64_t last_thread_id;\r
135  \r
136 @@ -55,6 +59,57 @@ struct _notmuch_database {\r
137      Xapian::ValueRangeProcessor *date_range_processor;\r
138  };\r
139  \r
140 +/* Bit masks for _notmuch_database::features. */\r
141 +enum {\r
142 +    /* If set, file names are stored in "file-direntry" terms.  If\r
143 +     * unset, file names are stored in document data.\r
144 +     *\r
145 +     * Introduced: version 1.  Implementation support: both for read;\r
146 +     * required for write. */\r
147 +    NOTMUCH_FEATURE_FILE_TERMS = 1 << 0,\r
148 +\r
149 +    /* If set, directory timestamps are stored in documents with\r
150 +     * XDIRECTORY terms and relative paths.  If unset, directory\r
151 +     * timestamps are stored in documents with XTIMESTAMP terms and\r
152 +     * absolute paths.\r
153 +     *\r
154 +     * Introduced: version 1.  Implementation support: required. */\r
155 +    NOTMUCH_FEATURE_DIRECTORY_DOCS = 1 << 1,\r
156 +\r
157 +    /* If set, the from, subject, and message-id headers are stored in\r
158 +     * message document values.  If unset, message documents *may*\r
159 +     * have these values, but if the value is empty, it must be\r
160 +     * retrieved from the message file.\r
161 +     *\r
162 +     * Introduced: optional in version 1, required as of version 3.\r
163 +     * Implementation support: both.\r
164 +     */\r
165 +    NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES = 1 << 2,\r
166 +\r
167 +    /* If set, folder terms are boolean and path terms exist.  If\r
168 +     * unset, folder terms are probabilistic and stemmed and path\r
169 +     * terms do not exist.\r
170 +     *\r
171 +     * Introduced: version 2.  Implementation support: required. */\r
172 +    NOTMUCH_FEATURE_BOOL_FOLDER = 1 << 3,\r
173 +};\r
174 +\r
175 +/* Prior to database version 3, features were implied by the database\r
176 + * version number, so hard-code them for earlier versions. */\r
177 +#define NOTMUCH_FEATURES_V0 (0)\r
178 +#define NOTMUCH_FEATURES_V1 (NOTMUCH_FEATURES_V0 | NOTMUCH_FEATURE_FILE_TERMS | \\r
179 +                            NOTMUCH_FEATURE_DIRECTORY_DOCS)\r
180 +#define NOTMUCH_FEATURES_V2 (NOTMUCH_FEATURES_V1 | NOTMUCH_FEATURE_BOOL_FOLDER)\r
181 +\r
182 +/* Current database features.  If any of these are missing from a\r
183 + * database, request an upgrade.\r
184 + * NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES is not included because\r
185 + * upgrade doesn't currently introduce the feature (though brand new\r
186 + * databases will have it). */\r
187 +#define NOTMUCH_FEATURES_CURRENT \\r
188 +    (NOTMUCH_FEATURE_FILE_TERMS | NOTMUCH_FEATURE_DIRECTORY_DOCS | \\r
189 +     NOTMUCH_FEATURE_BOOL_FOLDER)\r
190 +\r
191  /* Return the list of terms from the given iterator matching a prefix.\r
192   * The prefix will be stripped from the strings in the returned list.\r
193   * The list will be allocated using ctx as the talloc context.\r
194 diff --git a/lib/database.cc b/lib/database.cc\r
195 index 45c4260..29a56db 100644\r
196 --- a/lib/database.cc\r
197 +++ b/lib/database.cc\r
198 @@ -20,6 +20,7 @@\r
199  \r
200  #include "database-private.h"\r
201  #include "parse-time-vrp.h"\r
202 +#include "string-util.h"\r
203  \r
204  #include <iostream>\r
205  \r
206 @@ -42,7 +43,7 @@ typedef struct {\r
207      const char *prefix;\r
208  } prefix_t;\r
209  \r
210 -#define NOTMUCH_DATABASE_VERSION 2\r
211 +#define NOTMUCH_DATABASE_VERSION 3\r
212  \r
213  #define STRINGIFY(s) _SUB_STRINGIFY(s)\r
214  #define _SUB_STRINGIFY(s) #s\r
215 @@ -151,6 +152,17 @@ typedef struct {\r
216   *                     changes are made to the database (such as by\r
217   *                     indexing new fields).\r
218   *\r
219 + *     features        The set of features supported by this\r
220 + *                     database. This consists of a set of\r
221 + *                     '\n'-separated lines, where each is a feature\r
222 + *                     name, a '\t', and compatibility flags.  If the\r
223 + *                     compatibility flags contain 'w', then the\r
224 + *                     opener must support this feature to safely\r
225 + *                     write this database.  If the compatibility\r
226 + *                     flags contain 'r', then the opener must\r
227 + *                     support this feature to read this database.\r
228 + *                     Introduced in database version 3.\r
229 + *\r
230   *     last_thread_id  The last thread ID generated. This is stored\r
231   *                     as a 16-byte hexadecimal ASCII representation\r
232   *                     of a 64-bit unsigned integer. The first ID\r
233 @@ -251,6 +263,25 @@ _find_prefix (const char *name)\r
234      return "";\r
235  }\r
236  \r
237 +static const struct\r
238 +{\r
239 +    /* NOTMUCH_FEATURE_* value. */\r
240 +    unsigned int value;\r
241 +    /* Feature name as it appears in the database.  This name should\r
242 +     * be appropriate for displaying to the user if an older version\r
243 +     * of notmuch doesn't support this feature. */\r
244 +    const char *name;\r
245 +    /* Compatibility flags when this feature is declared. */\r
246 +    const char *flags;\r
247 +} feature_names[] = {\r
248 +    {NOTMUCH_FEATURE_FILE_TERMS,             "multiple paths per message", "rw"},\r
249 +    {NOTMUCH_FEATURE_DIRECTORY_DOCS,         "relative directory paths", "rw"},\r
250 +    /* Header values are not required for reading a database because a\r
251 +     * reader can just refer to the message file. */\r
252 +    {NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES, "from/subject/message-ID in database", "w"},\r
253 +    {NOTMUCH_FEATURE_BOOL_FOLDER,            "exact folder:/path: search", "rw"},\r
254 +};\r
255 +\r
256  const char *\r
257  notmuch_status_to_string (notmuch_status_t status)\r
258  {\r
259 @@ -591,6 +622,11 @@ notmuch_database_create (const char *path, notmuch_database_t **database)\r
260                                     &notmuch);\r
261      if (status)\r
262         goto DONE;\r
263 +\r
264 +    /* Upgrade doesn't add this feature to existing databases, but new\r
265 +     * databases have it. */\r
266 +    notmuch->features |= NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES;\r
267 +\r
268      status = notmuch_database_upgrade (notmuch, NULL, NULL);\r
269      if (status) {\r
270         notmuch_database_close(notmuch);\r
271 @@ -619,6 +655,80 @@ _notmuch_database_ensure_writable (notmuch_database_t *notmuch)\r
272      return NOTMUCH_STATUS_SUCCESS;\r
273  }\r
274  \r
275 +/* Parse a database features string from the given database version.\r
276 + *\r
277 + * For version < 3, this ignores the features string and returns a\r
278 + * hard-coded set of features.\r
279 + *\r
280 + * If there are unrecognized features that are required to open the\r
281 + * database in mode (which should be 'r' or 'w'), return a\r
282 + * comma-separated list of unrecognized but required features in\r
283 + * *incompat_out, which will be allocated from ctx.\r
284 + */\r
285 +static unsigned int\r
286 +_parse_features (const void *ctx, const char *features, unsigned int version,\r
287 +                char mode, char **incompat_out)\r
288 +{\r
289 +    unsigned int res = 0, namelen, i;\r
290 +    size_t llen = 0;\r
291 +    const char *flags;\r
292 +\r
293 +    /* Prior to database version 3, features were implied by the\r
294 +     * version number. */\r
295 +    if (version == 0)\r
296 +       return NOTMUCH_FEATURES_V0;\r
297 +    else if (version == 1)\r
298 +       return NOTMUCH_FEATURES_V1;\r
299 +    else if (version == 2)\r
300 +       return NOTMUCH_FEATURES_V2;\r
301 +\r
302 +    /* Parse the features string */\r
303 +    while ((features = strtok_len_c (features + llen, "\n", &llen)) != NULL) {\r
304 +       flags = strchr (features, '\t');\r
305 +       if (! flags || flags > features + llen)\r
306 +           continue;\r
307 +       namelen = flags - features;\r
308 +\r
309 +       for (i = 0; i < ARRAY_SIZE (feature_names); ++i) {\r
310 +           if (strlen (feature_names[i].name) == namelen &&\r
311 +               strncmp (feature_names[i].name, features, namelen) == 0) {\r
312 +               res |= feature_names[i].value;\r
313 +               break;\r
314 +           }\r
315 +       }\r
316 +\r
317 +       if (i == ARRAY_SIZE (feature_names)) {\r
318 +           /* Unrecognized feature */\r
319 +           const char *have = strchr (flags, mode);\r
320 +           if (have && have < features + llen) {\r
321 +               /* This feature is required to access this database in\r
322 +                * 'mode', but we don't understand it. */\r
323 +               if (! *incompat_out)\r
324 +                   *incompat_out = talloc_strdup (ctx, "");\r
325 +               *incompat_out = talloc_asprintf_append_buffer (\r
326 +                   *incompat_out, "%s%.*s", **incompat_out ? ", " : "",\r
327 +                   namelen, features);\r
328 +           }\r
329 +       }\r
330 +    }\r
331 +\r
332 +    return res;\r
333 +}\r
334 +\r
335 +static char *\r
336 +_print_features (const void *ctx, unsigned int features)\r
337 +{\r
338 +    unsigned int i;\r
339 +    char *res = talloc_strdup (ctx, "");\r
340 +\r
341 +    for (i = 0; i < ARRAY_SIZE (feature_names); ++i)\r
342 +       if (features & feature_names[i].value)\r
343 +           res = talloc_asprintf_append_buffer (\r
344 +               res, "%s\t%s\n", feature_names[i].name, feature_names[i].flags);\r
345 +\r
346 +    return res;\r
347 +}\r
348 +\r
349  notmuch_status_t\r
350  notmuch_database_open (const char *path,\r
351                        notmuch_database_mode_t mode,\r
352 @@ -627,7 +737,7 @@ notmuch_database_open (const char *path,\r
353      notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;\r
354      void *local = talloc_new (NULL);\r
355      notmuch_database_t *notmuch = NULL;\r
356 -    char *notmuch_path, *xapian_path;\r
357 +    char *notmuch_path, *xapian_path, *incompat_features;\r
358      struct stat st;\r
359      int err;\r
360      unsigned int i, version;\r
361 @@ -677,7 +787,6 @@ notmuch_database_open (const char *path,\r
362      if (notmuch->path[strlen (notmuch->path) - 1] == '/')\r
363         notmuch->path[strlen (notmuch->path) - 1] = '\0';\r
364  \r
365 -    notmuch->needs_upgrade = FALSE;\r
366      notmuch->mode = mode;\r
367      notmuch->atomic_nesting = 0;\r
368      try {\r
369 @@ -686,37 +795,44 @@ notmuch_database_open (const char *path,\r
370         if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {\r
371             notmuch->xapian_db = new Xapian::WritableDatabase (xapian_path,\r
372                                                                Xapian::DB_CREATE_OR_OPEN);\r
373 -           version = notmuch_database_get_version (notmuch);\r
374 -\r
375 -           if (version > NOTMUCH_DATABASE_VERSION) {\r
376 -               fprintf (stderr,\r
377 -                        "Error: Notmuch database at %s\n"\r
378 -                        "       has a newer database format version (%u) than supported by this\n"\r
379 -                        "       version of notmuch (%u). Refusing to open this database in\n"\r
380 -                        "       read-write mode.\n",\r
381 -                        notmuch_path, version, NOTMUCH_DATABASE_VERSION);\r
382 -               notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY;\r
383 -               notmuch_database_destroy (notmuch);\r
384 -               notmuch = NULL;\r
385 -               status = NOTMUCH_STATUS_FILE_ERROR;\r
386 -               goto DONE;\r
387 -           }\r
388 -\r
389 -           if (version < NOTMUCH_DATABASE_VERSION)\r
390 -               notmuch->needs_upgrade = TRUE;\r
391         } else {\r
392             notmuch->xapian_db = new Xapian::Database (xapian_path);\r
393 -           version = notmuch_database_get_version (notmuch);\r
394 -           if (version > NOTMUCH_DATABASE_VERSION)\r
395 -           {\r
396 -               fprintf (stderr,\r
397 -                        "Warning: Notmuch database at %s\n"\r
398 -                        "         has a newer database format version (%u) than supported by this\n"\r
399 -                        "         version of notmuch (%u). Some operations may behave incorrectly,\n"\r
400 -                        "         (but the database will not be harmed since it is being opened\n"\r
401 -                        "         in read-only mode).\n",\r
402 -                        notmuch_path, version, NOTMUCH_DATABASE_VERSION);\r
403 -           }\r
404 +       }\r
405 +\r
406 +       /* Check version.  As of database version 3, we represent\r
407 +        * changes in terms of features, so assume a version bump\r
408 +        * means a dramatically incompatible change. */\r
409 +       version = notmuch_database_get_version (notmuch);\r
410 +       if (version > NOTMUCH_DATABASE_VERSION) {\r
411 +           fprintf (stderr,\r
412 +                    "Error: Notmuch database at %s\n"\r
413 +                    "       has a newer database format version (%u) than supported by this\n"\r
414 +                    "       version of notmuch (%u).\n",\r
415 +                    notmuch_path, version, NOTMUCH_DATABASE_VERSION);\r
416 +           notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY;\r
417 +           notmuch_database_destroy (notmuch);\r
418 +           notmuch = NULL;\r
419 +           status = NOTMUCH_STATUS_FILE_ERROR;\r
420 +           goto DONE;\r
421 +       }\r
422 +\r
423 +       /* Check features. */\r
424 +       incompat_features = NULL;\r
425 +       notmuch->features = _parse_features (\r
426 +           local, notmuch->xapian_db->get_metadata ("features").c_str (),\r
427 +           version, mode == NOTMUCH_DATABASE_MODE_READ_WRITE ? 'w' : 'r',\r
428 +           &incompat_features);\r
429 +       if (incompat_features) {\r
430 +           fprintf (stderr,\r
431 +                    "Error: Notmuch database at %s\n"\r
432 +                    "       requires features (%s)\n"\r
433 +                    "       not supported by this version of notmuch.\n",\r
434 +                    notmuch_path, incompat_features);\r
435 +           notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY;\r
436 +           notmuch_database_destroy (notmuch);\r
437 +           notmuch = NULL;\r
438 +           status = NOTMUCH_STATUS_FILE_ERROR;\r
439 +           goto DONE;\r
440         }\r
441  \r
442         notmuch->last_doc_id = notmuch->xapian_db->get_lastdocid ();\r
443 @@ -1048,7 +1164,8 @@ notmuch_database_get_version (notmuch_database_t *notmuch)\r
444  notmuch_bool_t\r
445  notmuch_database_needs_upgrade (notmuch_database_t *notmuch)\r
446  {\r
447 -    return notmuch->needs_upgrade;\r
448 +    return notmuch->mode == NOTMUCH_DATABASE_MODE_READ_WRITE &&\r
449 +       (NOTMUCH_FEATURES_CURRENT & ~notmuch->features);\r
450  }\r
451  \r
452  static volatile sig_atomic_t do_progress_notify = 0;\r
453 @@ -1077,6 +1194,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
454                                                    double progress),\r
455                           void *closure)\r
456  {\r
457 +    void *local = talloc_new (NULL);\r
458      Xapian::WritableDatabase *db;\r
459      struct sigaction action;\r
460      struct itimerval timerval;\r
461 @@ -1114,6 +1232,10 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
462         timer_is_active = TRUE;\r
463      }\r
464  \r
465 +    /* Set the target features so we write out changes in the desired\r
466 +     * format. */\r
467 +    notmuch->features |= NOTMUCH_FEATURES_CURRENT;\r
468 +\r
469      /* Before version 1, each message document had its filename in the\r
470       * data field. Copy that into the new format by calling\r
471       * notmuch_message_add_filename.\r
472 @@ -1226,6 +1348,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
473         notmuch_query_destroy (query);\r
474      }\r
475  \r
476 +    db->set_metadata ("features", _print_features (local, notmuch->features));\r
477      db->set_metadata ("version", STRINGIFY (NOTMUCH_DATABASE_VERSION));\r
478      db->flush ();\r
479  \r
480 @@ -1302,6 +1425,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
481         sigaction (SIGALRM, &action, NULL);\r
482      }\r
483  \r
484 +    talloc_free (local);\r
485      return NOTMUCH_STATUS_SUCCESS;\r
486  }\r
487  \r
488 -- \r
489 2.0.0\r
490 \r