Re: [PATCH 05/14] lib: Database version 3: Introduce fine-grained "features"
authorMark Walters <markwalters1009@gmail.com>
Sun, 27 Jul 2014 09:53:25 +0000 (10:53 +0100)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 18:03:54 +0000 (10:03 -0800)
ce/94cd64c5cca99581a557d4abb5db398282330c [new file with mode: 0644]

diff --git a/ce/94cd64c5cca99581a557d4abb5db398282330c b/ce/94cd64c5cca99581a557d4abb5db398282330c
new file mode 100644 (file)
index 0000000..4ddc3bf
--- /dev/null
@@ -0,0 +1,496 @@
+Return-Path: <m.walters@qmul.ac.uk>\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 1D35E431FBC\r
+       for <notmuch@notmuchmail.org>; Sun, 27 Jul 2014 02:54:16 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0.502\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0.502 tagged_above=-999 required=5\r
+       tests=[DKIM_ADSP_CUSTOM_MED=0.001, FREEMAIL_FROM=0.001,\r
+       NML_ADSP_CUSTOM_MED=1.2, RCVD_IN_DNSWL_LOW=-0.7] 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 Pp9Dr19U+OZr for <notmuch@notmuchmail.org>;\r
+       Sun, 27 Jul 2014 02:54:08 -0700 (PDT)\r
+Received: from mail2.qmul.ac.uk (mail2.qmul.ac.uk [138.37.6.6])\r
+       (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
+       (No client certificate requested)\r
+       by olra.theworths.org (Postfix) with ESMTPS id 97910431FB6\r
+       for <notmuch@notmuchmail.org>; Sun, 27 Jul 2014 02:54:07 -0700 (PDT)\r
+Received: from smtp.qmul.ac.uk ([138.37.6.40])\r
+       by mail2.qmul.ac.uk with esmtp (Exim 4.71)\r
+       (envelope-from <m.walters@qmul.ac.uk>)\r
+       id 1XBL9F-0004Fl-0B; Sun, 27 Jul 2014 10:54:05 +0100\r
+Received: from 94.196.249.126.threembb.co.uk ([94.196.249.126] helo=localhost)\r
+       by smtp.qmul.ac.uk with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.71)\r
+       (envelope-from <m.walters@qmul.ac.uk>)\r
+       id 1XBL98-0006EF-Eg; Sun, 27 Jul 2014 10:53:44 +0100\r
+From: Mark Walters <markwalters1009@gmail.com>\r
+To: Austin Clements <amdragon@MIT.EDU>, notmuch@notmuchmail.org\r
+Subject: Re: [PATCH 05/14] lib: Database version 3: Introduce fine-grained\r
+       "features"\r
+In-Reply-To: <1406433173-19169-6-git-send-email-amdragon@mit.edu>\r
+References: <1406433173-19169-1-git-send-email-amdragon@mit.edu>\r
+       <1406433173-19169-6-git-send-email-amdragon@mit.edu>\r
+User-Agent: Notmuch/0.15.2+615~g78e3a93 (http://notmuchmail.org) Emacs/23.4.1\r
+       (x86_64-pc-linux-gnu)\r
+Date: Sun, 27 Jul 2014 10:53:25 +0100\r
+Message-ID: <87silnyvoq.fsf@qmul.ac.uk>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=us-ascii\r
+X-Sender-Host-Address: 94.196.249.126\r
+X-QM-Geographic: According to ripencc,\r
+       this message was delivered by a machine in Britain (UK) (GB).\r
+X-QM-SPAM-Info: Sender has good ham record.  :)\r
+X-QM-Body-MD5: 74279823180b19ebad2f59792d4c5cc2 (of first 20000 bytes)\r
+X-SpamAssassin-Score: -0.0\r
+X-SpamAssassin-SpamBar: /\r
+X-SpamAssassin-Report: The QM spam filters have analysed this message to\r
+       determine if it is\r
+       spam. We require at least 5.0 points to mark a message as spam.\r
+       This message scored -0.0 points.\r
+       Summary of the scoring: \r
+       * 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail\r
+       provider *      (markwalters1009[at]gmail.com)\r
+       * -0.0 AWL AWL: From: address is in the auto white-list\r
+X-QM-Scan-Virus: ClamAV says the message is clean\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: Sun, 27 Jul 2014 09:54:16 -0000\r
+\r
+\r
+On Sun, 27 Jul 2014, Austin Clements <amdragon@MIT.EDU> wrote:\r
+> Previously, our database schema was versioned by a single number.\r
+> Each database schema change had to occur "atomically" in Notmuch's\r
+> development history: before some commit, Notmuch used version N, after\r
+> that commit, it used version N+1.  Hence, each new schema version\r
+> could introduce only one change, the task of developing a schema\r
+> change fell on a single person, and it all had to happen and be\r
+> perfect in a single commit series.  This made introducing a new schema\r
+> version hard.  We've seen only two schema changes in the history of\r
+> Notmuch.\r
+\r
+I like this series as a whole and I am happy with all the patches that I\r
+haven't sent comments on. (Note, however, that I am not very familiar\r
+with the database code.)\r
+\r
+There is one thing which confuses me in this patch:\r
+\r
+>\r
+> This commit introduces database schema version 3; hopefully the last\r
+> schema version we'll need for a while.  With this version, we switch\r
+> from a single version number to "features": a set of named,\r
+> independent aspects of the database schema.\r
+>\r
+> Features should make backwards compatibility easier.  For many things,\r
+> it should be easy to support databases both with and without a\r
+> feature, which will allow us to make upgrades optional and will enable\r
+> "unstable" features that can be developed and tested over time.\r
+>\r
+> Features also make forwards compatibility easier.  The features\r
+> recorded in a database include "compatibility flags," which can\r
+> indicate to an older version of Notmuch when it must support a given\r
+> feature to open the database for read or for write.  This lets us\r
+> replace the old vague "I don't recognize this version, so something\r
+> might go wrong, but I promise to try my best" warnings upon opening a\r
+> database with an unknown version with precise errors.  If a database\r
+> is safe to open for read/write despite unknown features, an older\r
+> version will know that and issue no message at all.  If the database\r
+> is not safe to open for read/write because of unknown features, an\r
+> older version will know that, too, and can tell the user exactly which\r
+> required features it lacks support for.\r
+> ---\r
+>  lib/database-private.h |  56 +++++++++++++++\r
+>  lib/database.cc        | 189 +++++++++++++++++++++++++++++++++++++++++--------\r
+>  2 files changed, 214 insertions(+), 31 deletions(-)\r
+>\r
+> diff --git a/lib/database-private.h b/lib/database-private.h\r
+> index d3e65fd..323b9fe 100644\r
+> --- a/lib/database-private.h\r
+> +++ b/lib/database-private.h\r
+> @@ -46,6 +46,11 @@ struct _notmuch_database {\r
+>      int atomic_nesting;\r
+>      Xapian::Database *xapian_db;\r
+>  \r
+> +    /* Bit mask of features used by this database.  Features are\r
+> +     * named, independent aspects of the database schema.  This is a\r
+> +     * bitwise-OR of NOTMUCH_FEATURE_* values (below). */\r
+> +    unsigned int features;\r
+> +\r
+>      unsigned int last_doc_id;\r
+>      uint64_t last_thread_id;\r
+>  \r
+> @@ -55,6 +60,57 @@ struct _notmuch_database {\r
+>      Xapian::ValueRangeProcessor *date_range_processor;\r
+>  };\r
+>  \r
+> +/* Bit masks for _notmuch_database::features. */\r
+> +enum {\r
+> +    /* If set, file names are stored in "file-direntry" terms.  If\r
+> +     * unset, file names are stored in document data.\r
+> +     *\r
+> +     * Introduced: version 1.  Implementation support: both for read;\r
+> +     * required for write. */\r
+> +    NOTMUCH_FEATURE_FILE_TERMS = 1 << 0,\r
+> +\r
+> +    /* If set, directory timestamps are stored in documents with\r
+> +     * XDIRECTORY terms and relative paths.  If unset, directory\r
+> +     * timestamps are stored in documents with XTIMESTAMP terms and\r
+> +     * absolute paths.\r
+> +     *\r
+> +     * Introduced: version 1.  Implementation support: required. */\r
+> +    NOTMUCH_FEATURE_DIRECTORY_DOCS = 1 << 1,\r
+> +\r
+> +    /* If set, the from, subject, and message-id headers are stored in\r
+> +     * message document values.  If unset, message documents *may*\r
+> +     * have these values, but if the value is empty, it must be\r
+> +     * retrieved from the message file.\r
+> +     *\r
+> +     * Introduced: optional in version 1, required as of version 3.\r
+> +     * Implementation support: both.\r
+> +     */\r
+> +    NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES = 1 << 2,\r
+> +\r
+> +    /* If set, folder terms are boolean and path terms exist.  If\r
+> +     * unset, folder terms are probabilistic and stemmed and path\r
+> +     * terms do not exist.\r
+> +     *\r
+> +     * Introduced: version 2.  Implementation support: required. */\r
+> +    NOTMUCH_FEATURE_BOOL_FOLDER = 1 << 3,\r
+> +};\r
+> +\r
+> +/* Prior to database version 3, features were implied by the database\r
+> + * version number, so hard-code them for earlier versions. */\r
+> +#define NOTMUCH_FEATURES_V0 (0)\r
+> +#define NOTMUCH_FEATURES_V1 (NOTMUCH_FEATURES_V0 | NOTMUCH_FEATURE_FILE_TERMS | \\r
+> +                         NOTMUCH_FEATURE_DIRECTORY_DOCS)\r
+> +#define NOTMUCH_FEATURES_V2 (NOTMUCH_FEATURES_V1 | NOTMUCH_FEATURE_BOOL_FOLDER)\r
+> +\r
+> +/* Current database features.  If any of these are missing from a\r
+> + * database, request an upgrade.\r
+> + * NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES is not included because\r
+> + * upgrade doesn't currently introduce the feature (though brand new\r
+> + * databases will have it). */\r
+> +#define NOTMUCH_FEATURES_CURRENT \\r
+> +    (NOTMUCH_FEATURE_FILE_TERMS | NOTMUCH_FEATURE_DIRECTORY_DOCS | \\r
+> +     NOTMUCH_FEATURE_BOOL_FOLDER)\r
+> +\r
+>  /* Return the list of terms from the given iterator matching a prefix.\r
+>   * The prefix will be stripped from the strings in the returned list.\r
+>   * The list will be allocated using ctx as the talloc context.\r
+> diff --git a/lib/database.cc b/lib/database.cc\r
+> index 45c4260..03eef3e 100644\r
+> --- a/lib/database.cc\r
+> +++ b/lib/database.cc\r
+> @@ -20,6 +20,7 @@\r
+>  \r
+>  #include "database-private.h"\r
+>  #include "parse-time-vrp.h"\r
+> +#include "string-util.h"\r
+>  \r
+>  #include <iostream>\r
+>  \r
+> @@ -42,7 +43,7 @@ typedef struct {\r
+>      const char *prefix;\r
+>  } prefix_t;\r
+>  \r
+> -#define NOTMUCH_DATABASE_VERSION 2\r
+> +#define NOTMUCH_DATABASE_VERSION 3\r
+>  \r
+>  #define STRINGIFY(s) _SUB_STRINGIFY(s)\r
+>  #define _SUB_STRINGIFY(s) #s\r
+> @@ -151,6 +152,17 @@ typedef struct {\r
+>   *                  changes are made to the database (such as by\r
+>   *                  indexing new fields).\r
+>   *\r
+> + *  features        The set of features supported by this\r
+> + *                  database. This consists of a set of\r
+> + *                  '\n'-separated lines, where each is a feature\r
+> + *                  name, a '\t', and compatibility flags.  If the\r
+> + *                  compatibility flags contain 'w', then the\r
+> + *                  opener must support this feature to safely\r
+> + *                  write this database.  If the compatibility\r
+> + *                  flags contain 'r', then the opener must\r
+> + *                  support this feature to read this database.\r
+> + *                  Introduced in database version 3.\r
+> + *\r
+>   *  last_thread_id  The last thread ID generated. This is stored\r
+>   *                  as a 16-byte hexadecimal ASCII representation\r
+>   *                  of a 64-bit unsigned integer. The first ID\r
+> @@ -226,6 +238,7 @@ static prefix_t PROBABILISTIC_PREFIX[]= {\r
+>      { "subject",            "XSUBJECT"},\r
+>  };\r
+>  \r
+> +\r
+>  const char *\r
+>  _find_prefix (const char *name)\r
+>  {\r
+> @@ -251,6 +264,25 @@ _find_prefix (const char *name)\r
+>      return "";\r
+>  }\r
+>  \r
+> +static const struct\r
+> +{\r
+> +    /* NOTMUCH_FEATURE_* value. */\r
+> +    unsigned int value;\r
+> +    /* Feature name as it appears in the database.  This name should\r
+> +     * be appropriate for displaying to the user if an older version\r
+> +     * of notmuch doesn't support this feature. */\r
+> +    const char *name;\r
+> +    /* Compatibility flags when this feature is declared. */\r
+> +    const char *flags;\r
+> +} feature_names[] = {\r
+> +    {NOTMUCH_FEATURE_FILE_TERMS,             "file terms", "rw"},\r
+> +    {NOTMUCH_FEATURE_DIRECTORY_DOCS,         "directory documents", "rw"},\r
+> +    /* Header values are not required for reading a database because a\r
+> +     * reader can just refer to the message file. */\r
+> +    {NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES, "from/subject/ID values", "w"},\r
+> +    {NOTMUCH_FEATURE_BOOL_FOLDER,            "boolean folder terms", "rw"},\r
+> +};\r
+> +\r
+>  const char *\r
+>  notmuch_status_to_string (notmuch_status_t status)\r
+>  {\r
+> @@ -591,6 +623,11 @@ notmuch_database_create (const char *path, notmuch_database_t **database)\r
+>                                  &notmuch);\r
+>      if (status)\r
+>      goto DONE;\r
+> +\r
+> +    /* Upgrade doesn't add this feature to existing databases, but new\r
+> +     * databases have it. */\r
+> +    notmuch->features |= NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES;\r
+> +\r
+\r
+Does this mean that if the user wants this feature he has to dump,\r
+delete the existing database, and restore? Is that just because no-one\r
+has implemented upgrade for this feature or is it "hard" in some sense?\r
+\r
+A possibly related question: is there likely to be a case where the user\r
+does not want to add/upgrade some feature? Would it be worth having an\r
+explicit upgrade command which let the user choose which features to\r
+upgrade? This is orthogonal to this series: I am just trying to get a\r
+feel for how it will, or could, be used.\r
+\r
+Best wishes\r
+\r
+Mark\r
+\r
+>      status = notmuch_database_upgrade (notmuch, NULL, NULL);\r
+>      if (status) {\r
+>      notmuch_database_close(notmuch);\r
+> @@ -619,6 +656,80 @@ _notmuch_database_ensure_writable (notmuch_database_t *notmuch)\r
+>      return NOTMUCH_STATUS_SUCCESS;\r
+>  }\r
+>  \r
+> +/* Parse a database features string from the given database version.\r
+> + *\r
+> + * For version < 3, this ignores the features string and returns a\r
+> + * hard-coded set of features.\r
+> + *\r
+> + * If there are unrecognized features that are required to open the\r
+> + * database in mode (which should be 'r' or 'w'), return a\r
+> + * comma-separated list of unrecognized but required features in\r
+> + * *incompat_out, which will be allocated from ctx.\r
+> + */\r
+> +static unsigned int\r
+> +_parse_features (const void *ctx, const char *features, unsigned int version,\r
+> +             char mode, char **incompat_out)\r
+> +{\r
+> +    unsigned int res = 0, namelen, i;\r
+> +    size_t llen = 0;\r
+> +    const char *flags;\r
+> +\r
+> +    /* Prior to database version 3, features were implied by the\r
+> +     * version number. */\r
+> +    if (version == 0)\r
+> +    return NOTMUCH_FEATURES_V0;\r
+> +    else if (version == 1)\r
+> +    return NOTMUCH_FEATURES_V1;\r
+> +    else if (version == 2)\r
+> +    return NOTMUCH_FEATURES_V2;\r
+> +\r
+> +    /* Parse the features string */\r
+> +    while ((features = strtok_len_c (features + llen, "\n", &llen)) != NULL) {\r
+> +    flags = strchr (features, '\t');\r
+> +    if (! flags || flags > features + llen)\r
+> +        continue;\r
+> +    namelen = flags - features;\r
+> +\r
+> +    for (i = 0; i < ARRAY_SIZE (feature_names); ++i) {\r
+> +        if (strlen (feature_names[i].name) == namelen &&\r
+> +            strncmp (feature_names[i].name, features, namelen) == 0) {\r
+> +            res |= feature_names[i].value;\r
+> +            break;\r
+> +        }\r
+> +    }\r
+> +\r
+> +    if (i == ARRAY_SIZE (feature_names)) {\r
+> +        /* Unrecognized feature */\r
+> +        const char *have = strchr (flags, mode);\r
+> +        if (have && have < features + llen) {\r
+> +            /* This feature is required to access this database in\r
+> +             * 'mode', but we don't understand it. */\r
+> +            if (! *incompat_out)\r
+> +                *incompat_out = talloc_strdup (ctx, "");\r
+> +            *incompat_out = talloc_asprintf_append_buffer (\r
+> +                *incompat_out, "%s%.*s", **incompat_out ? ", " : "",\r
+> +                namelen, features);\r
+> +        }\r
+> +    }\r
+> +    }\r
+> +\r
+> +    return res;\r
+> +}\r
+> +\r
+> +static char *\r
+> +_print_features (const void *ctx, unsigned int features)\r
+> +{\r
+> +    unsigned int i;\r
+> +    char *res = talloc_strdup (ctx, "");\r
+> +\r
+> +    for (i = 0; i < ARRAY_SIZE (feature_names); ++i)\r
+> +    if (features & feature_names[i].value)\r
+> +        res = talloc_asprintf_append_buffer (\r
+> +            res, "%s\t%s\n", feature_names[i].name, feature_names[i].flags);\r
+> +\r
+> +    return res;\r
+> +}\r
+> +\r
+>  notmuch_status_t\r
+>  notmuch_database_open (const char *path,\r
+>                     notmuch_database_mode_t mode,\r
+> @@ -627,7 +738,7 @@ notmuch_database_open (const char *path,\r
+>      notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;\r
+>      void *local = talloc_new (NULL);\r
+>      notmuch_database_t *notmuch = NULL;\r
+> -    char *notmuch_path, *xapian_path;\r
+> +    char *notmuch_path, *xapian_path, *incompat_features;\r
+>      struct stat st;\r
+>      int err;\r
+>      unsigned int i, version;\r
+> @@ -686,39 +797,51 @@ notmuch_database_open (const char *path,\r
+>      if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {\r
+>          notmuch->xapian_db = new Xapian::WritableDatabase (xapian_path,\r
+>                                                             Xapian::DB_CREATE_OR_OPEN);\r
+> -        version = notmuch_database_get_version (notmuch);\r
+> -\r
+> -        if (version > NOTMUCH_DATABASE_VERSION) {\r
+> -            fprintf (stderr,\r
+> -                     "Error: Notmuch database at %s\n"\r
+> -                     "       has a newer database format version (%u) than supported by this\n"\r
+> -                     "       version of notmuch (%u). Refusing to open this database in\n"\r
+> -                     "       read-write mode.\n",\r
+> -                     notmuch_path, version, NOTMUCH_DATABASE_VERSION);\r
+> -            notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY;\r
+> -            notmuch_database_destroy (notmuch);\r
+> -            notmuch = NULL;\r
+> -            status = NOTMUCH_STATUS_FILE_ERROR;\r
+> -            goto DONE;\r
+> -        }\r
+> -\r
+> -        if (version < NOTMUCH_DATABASE_VERSION)\r
+> -            notmuch->needs_upgrade = TRUE;\r
+>      } else {\r
+>          notmuch->xapian_db = new Xapian::Database (xapian_path);\r
+> -        version = notmuch_database_get_version (notmuch);\r
+> -        if (version > NOTMUCH_DATABASE_VERSION)\r
+> -        {\r
+> -            fprintf (stderr,\r
+> -                     "Warning: Notmuch database at %s\n"\r
+> -                     "         has a newer database format version (%u) than supported by this\n"\r
+> -                     "         version of notmuch (%u). Some operations may behave incorrectly,\n"\r
+> -                     "         (but the database will not be harmed since it is being opened\n"\r
+> -                     "         in read-only mode).\n",\r
+> -                     notmuch_path, version, NOTMUCH_DATABASE_VERSION);\r
+> -        }\r
+>      }\r
+>  \r
+> +    /* Check version.  As of database version 3, we represent\r
+> +     * changes in terms of features, so assume a version bump\r
+> +     * means a dramatically incompatible change. */\r
+> +    version = notmuch_database_get_version (notmuch);\r
+> +    if (version > NOTMUCH_DATABASE_VERSION) {\r
+> +        fprintf (stderr,\r
+> +                 "Error: Notmuch database at %s\n"\r
+> +                 "       has a newer database format version (%u) than supported by this\n"\r
+> +                 "       version of notmuch (%u).\n",\r
+> +                 notmuch_path, version, NOTMUCH_DATABASE_VERSION);\r
+> +        notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY;\r
+> +        notmuch_database_destroy (notmuch);\r
+> +        notmuch = NULL;\r
+> +        status = NOTMUCH_STATUS_FILE_ERROR;\r
+> +        goto DONE;\r
+> +    }\r
+> +\r
+> +    /* Check features. */\r
+> +    incompat_features = NULL;\r
+> +    notmuch->features = _parse_features (\r
+> +        local, notmuch->xapian_db->get_metadata ("features").c_str (),\r
+> +        version, mode == NOTMUCH_DATABASE_MODE_READ_WRITE ? 'w' : 'r',\r
+> +        &incompat_features);\r
+> +    if (incompat_features) {\r
+> +        fprintf (stderr,\r
+> +                 "Error: Notmuch database at %s\n"\r
+> +                 "       requires features (%s)\n"\r
+> +                 "       not supported by this version of notmuch.\n",\r
+> +                 notmuch_path, incompat_features);\r
+> +        notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY;\r
+> +        notmuch_database_destroy (notmuch);\r
+> +        notmuch = NULL;\r
+> +        status = NOTMUCH_STATUS_FILE_ERROR;\r
+> +        goto DONE;\r
+> +    }\r
+> +\r
+> +    /* Do we want an upgrade? */\r
+> +    if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE &&\r
+> +        NOTMUCH_FEATURES_CURRENT & ~notmuch->features)\r
+> +        notmuch->needs_upgrade = TRUE;\r
+> +\r
+>      notmuch->last_doc_id = notmuch->xapian_db->get_lastdocid ();\r
+>      last_thread_id = notmuch->xapian_db->get_metadata ("last_thread_id");\r
+>      if (last_thread_id.empty ()) {\r
+> @@ -1077,6 +1200,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
+>                                                 double progress),\r
+>                        void *closure)\r
+>  {\r
+> +    void *local = talloc_new (NULL);\r
+>      Xapian::WritableDatabase *db;\r
+>      struct sigaction action;\r
+>      struct itimerval timerval;\r
+> @@ -1226,6 +1350,8 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
+>      notmuch_query_destroy (query);\r
+>      }\r
+>  \r
+> +    notmuch->features |= NOTMUCH_FEATURES_CURRENT;\r
+> +    db->set_metadata ("features", _print_features (local, notmuch->features));\r
+>      db->set_metadata ("version", STRINGIFY (NOTMUCH_DATABASE_VERSION));\r
+>      db->flush ();\r
+>  \r
+> @@ -1302,6 +1428,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
+>      sigaction (SIGALRM, &action, NULL);\r
+>      }\r
+>  \r
+> +    talloc_free (local);\r
+>      return NOTMUCH_STATUS_SUCCESS;\r
+>  }\r
+>  \r
+> -- \r
+> 2.0.0\r
+>\r
+> _______________________________________________\r
+> notmuch mailing list\r
+> notmuch@notmuchmail.org\r
+> http://notmuchmail.org/mailman/listinfo/notmuch\r