[PATCH v4 00/11] Implement and use database "features"
authorAustin Clements <amdragon@mit.edu>
Mon, 25 Aug 2014 17:25:58 +0000 (13:25 +2000)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 18:04:18 +0000 (10:04 -0800)
df/2c71fbaaaccc0cf587a989289ca804379cd7a4 [new file with mode: 0644]

diff --git a/df/2c71fbaaaccc0cf587a989289ca804379cd7a4 b/df/2c71fbaaaccc0cf587a989289ca804379cd7a4
new file mode 100644 (file)
index 0000000..3e2827d
--- /dev/null
@@ -0,0 +1,355 @@
+Return-Path: <amdragon@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 1219F431FC2\r
+       for <notmuch@notmuchmail.org>; Mon, 25 Aug 2014 10:26:23 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -2.3\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-2.3 tagged_above=-999 required=5\r
+       tests=[RCVD_IN_DNSWL_MED=-2.3] 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 mjCZ7ZBNKWTt for <notmuch@notmuchmail.org>;\r
+       Mon, 25 Aug 2014 10:26:15 -0700 (PDT)\r
+Received: from dmz-mailsec-scanner-8.mit.edu (dmz-mailsec-scanner-8.mit.edu\r
+       [18.7.68.37])\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 77029431FAE\r
+       for <notmuch@notmuchmail.org>; Mon, 25 Aug 2014 10:26:15 -0700 (PDT)\r
+X-AuditID: 12074425-f79e46d000002583-15-53fb71b67eaa\r
+Received: from mailhub-auth-1.mit.edu ( [18.9.21.35])\r
+       (using TLS with cipher AES256-SHA (256/256 bits))\r
+       (Client did not present a certificate)\r
+       by dmz-mailsec-scanner-8.mit.edu (Symantec Messaging Gateway) with SMTP\r
+       id 1B.7E.09603.6B17BF35; Mon, 25 Aug 2014 13:26:14 -0400 (EDT)\r
+Received: from outgoing.mit.edu (outgoing-auth-1.mit.edu [18.9.28.11])\r
+       by mailhub-auth-1.mit.edu (8.13.8/8.9.2) with ESMTP id s7PHQDsW007792; \r
+       Mon, 25 Aug 2014 13:26:14 -0400\r
+Received: from drake.dyndns.org (31-35-14.wireless.csail.mit.edu\r
+       [128.31.35.14]) (authenticated bits=0)\r
+       (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+       by outgoing.mit.edu (8.13.8/8.12.4) with ESMTP id s7PHQBp7029601\r
+       (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+       Mon, 25 Aug 2014 13:26:13 -0400\r
+Received: from amthrax by drake.dyndns.org with local (Exim 4.77)\r
+       (envelope-from <amdragon@mit.edu>)\r
+       id 1XLy1z-0003jH-RC; Mon, 25 Aug 2014 13:26:11 -0400\r
+From: Austin Clements <amdragon@mit.edu>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH v4 00/11] Implement and use database "features"\r
+Date: Mon, 25 Aug 2014 13:25:58 -0400\r
+Message-Id: <1408987569-14146-1-git-send-email-amdragon@mit.edu>\r
+X-Mailer: git-send-email 2.0.0\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFlrPIsWRmVeSWpSXmKPExsUixCmqrLut8HewwbVGQ4sbrd2MFk3TnS2u\r
+       35zJ7MDscev+a3aPZ6tuMXtsOfSeOYA5issmJTUnsyy1SN8ugStj/dKlbAWTgireLV3K0sD4\r
+       yaGLkZNDQsBEYuK7b+wQtpjEhXvr2boYuTiEBGYzSbw8c4gRwtnIKHFoxSUmCOcYk8SvWf1Q\r
+       ZXMZJda/7WQD6WcT0JD4fWsxE4gtIiAtsfPubFYQm1lATWL9n1dgO4QF7CVeP9gCVsMioCpx\r
+       /9RMsDivgIPEq7/zWSHukJNouPGJbQIj7wJGhlWMsim5Vbq5iZk5xanJusXJiXl5qUW6Fnq5\r
+       mSV6qSmlmxhB4cLuorqDccIhpUOMAhyMSjy8N+J/BwuxJpYVV+YeYpTkYFIS5Z2WDxTiS8pP\r
+       qcxILM6ILyrNSS0+xCjBwawkwtsMkuNNSaysSi3Kh0lJc7AoifO+tbYKFhJITyxJzU5NLUgt\r
+       gsnKcHAoSfDmFgA1ChalpqdWpGXmlCCkmTg4QYbzAA2PB6nhLS5IzC3OTIfIn2JUlBLn/Q+S\r
+       EABJZJTmwfXC4vkVozjQK8K8R0GqeICpAK77FdBgJqDBpj0/QQaXJCKkpBoYo10yz+1bWy+r\r
+       sOPZjIdb+puLdJqzZ7ctf3v9Sei8afs/qYdVfjn8692utP65y0oiipbnnxcNnLb+coLHq6r/\r
+       b7aLPP6kUlGw13qNGsvutT+iCozL9+isCnSbdtRV/GX7xW2Jh1ZNr/xpfEGiIVp8DeO3RPW2\r
+       3M5zqQtiPjO6nCnp+xDuOjGBQYmlOCPRUIu5qDgRADK9FEHCAgAA\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: Mon, 25 Aug 2014 17:26:23 -0000\r
+\r
+This is v4 of id:1406859003-11561-1-git-send-email-amdragon@mit.edu.\r
+In addition to rebasing to current master, this makes several tidying\r
+changes: it gives the features enum a name for better\r
+self-documentation and type-safety; improves the robustness of\r
+_parse_features to NULL pointers; requests upgrades if the database\r
+version is old, even if it supports all current features; improves\r
+various comments; and fixes some style errors.\r
+\r
+The diff from v3 is below.  Most of this diff relates to giving the\r
+enum a name, since it has to move above struct _notmuch_database and\r
+we need to define bitwise operators for C++.\r
+\r
+diff --git a/lib/database-private.h b/lib/database-private.h\r
+index 2ffab33..ca0751c 100644\r
+--- a/lib/database-private.h\r
++++ b/lib/database-private.h\r
+@@ -36,36 +36,30 @@\r
\r
+ #pragma GCC visibility push(hidden)\r
\r
+-struct _notmuch_database {\r
+-    notmuch_bool_t exception_reported;\r
+-\r
+-    char *path;\r
+-\r
+-    notmuch_database_mode_t mode;\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
+-    Xapian::QueryParser *query_parser;\r
+-    Xapian::TermGenerator *term_gen;\r
+-    Xapian::ValueRangeProcessor *value_range_processor;\r
+-    Xapian::ValueRangeProcessor *date_range_processor;\r
+-};\r
+-\r
+-/* Bit masks for _notmuch_database::features. */\r
+-enum {\r
++/* Bit masks for _notmuch_database::features.  Features are named,\r
++ * independent aspects of the database schema.\r
++ *\r
++ * A database stores the set of features that it "uses" (implicitly\r
++ * before database version 3 and explicitly as of version 3).\r
++ *\r
++ * A given library version will "recognize" a particular set of\r
++ * features; if a database uses a feature that the library does not\r
++ * recognize, the library will refuse to open it.  It is assumed the\r
++ * set of recognized features grows monotonically over time.  A\r
++ * library version will "implement" some subset of the recognized\r
++ * features: some operations may require that the database use (or not\r
++ * use) some feature, while other operations may support both\r
++ * databases that use and that don't use some feature.\r
++ *\r
++ * On disk, the database stores string names for these features (see\r
++ * the feature_names array).  These enum bit values are never\r
++ * persisted to disk and may change freely.\r
++ */\r
++enum _notmuch_features {\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
++     * Introduced: version 1. */\r
+     NOTMUCH_FEATURE_FILE_TERMS = 1 << 0,\r
\r
+     /* If set, directory timestamps are stored in documents with\r
+@@ -73,7 +67,7 @@ enum {\r
+      * timestamps are stored in documents with XTIMESTAMP terms and\r
+      * absolute paths.\r
+      *\r
+-     * Introduced: version 1.  Implementation support: required. */\r
++     * Introduced: version 1. */\r
+     NOTMUCH_FEATURE_DIRECTORY_DOCS = 1 << 1,\r
\r
+     /* If set, the from, subject, and message-id headers are stored in\r
+@@ -82,7 +76,6 @@ enum {\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
+@@ -90,13 +83,71 @@ enum {\r
+      * unset, folder terms are probabilistic and stemmed and path\r
+      * terms do not exist.\r
+      *\r
+-     * Introduced: version 2.  Implementation support: required. */\r
++     * Introduced: version 2. */\r
+     NOTMUCH_FEATURE_BOOL_FOLDER = 1 << 3,\r
+ };\r
\r
++/* In C++, a named enum is its own type, so define bitwise operators\r
++ * on _notmuch_features. */\r
++inline _notmuch_features\r
++operator|(_notmuch_features a, _notmuch_features b)\r
++{\r
++    return static_cast<_notmuch_features>(\r
++      static_cast<unsigned>(a) | static_cast<unsigned>(b));\r
++}\r
++\r
++inline _notmuch_features\r
++operator&(_notmuch_features a, _notmuch_features b)\r
++{\r
++    return static_cast<_notmuch_features>(\r
++      static_cast<unsigned>(a) & static_cast<unsigned>(b));\r
++}\r
++\r
++inline _notmuch_features\r
++operator~(_notmuch_features a)\r
++{\r
++    return static_cast<_notmuch_features>(~static_cast<unsigned>(a));\r
++}\r
++\r
++inline _notmuch_features&\r
++operator|=(_notmuch_features &a, _notmuch_features b)\r
++{\r
++    a = a | b;\r
++    return a;\r
++}\r
++\r
++inline _notmuch_features&\r
++operator&=(_notmuch_features &a, _notmuch_features b)\r
++{\r
++    a = a & b;\r
++    return a;\r
++}\r
++\r
++struct _notmuch_database {\r
++    notmuch_bool_t exception_reported;\r
++\r
++    char *path;\r
++\r
++    notmuch_database_mode_t mode;\r
++    int atomic_nesting;\r
++    Xapian::Database *xapian_db;\r
++\r
++    /* Bit mask of features used by this database.  This is a\r
++     * bitwise-OR of NOTMUCH_FEATURE_* values (above). */\r
++    enum _notmuch_features features;\r
++\r
++    unsigned int last_doc_id;\r
++    uint64_t last_thread_id;\r
++\r
++    Xapian::QueryParser *query_parser;\r
++    Xapian::TermGenerator *term_gen;\r
++    Xapian::ValueRangeProcessor *value_range_processor;\r
++    Xapian::ValueRangeProcessor *date_range_processor;\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_V0 ((enum _notmuch_features)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
+diff --git a/lib/database.cc b/lib/database.cc\r
+index 63257c2..5116188 100644\r
+--- a/lib/database.cc\r
++++ b/lib/database.cc\r
+@@ -266,10 +266,9 @@ _find_prefix (const char *name)\r
+     return "";\r
+ }\r
\r
+-static const struct\r
+-{\r
++static const struct {\r
+     /* NOTMUCH_FEATURE_* value. */\r
+-    unsigned int value;\r
++    _notmuch_features 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
+@@ -277,12 +276,16 @@ static const struct\r
+     /* Compatibility flags when this feature is declared. */\r
+     const char *flags;\r
+ } feature_names[] = {\r
+-    {NOTMUCH_FEATURE_FILE_TERMS,             "multiple paths per message", "rw"},\r
+-    {NOTMUCH_FEATURE_DIRECTORY_DOCS,         "relative directory paths", "rw"},\r
++    { NOTMUCH_FEATURE_FILE_TERMS,\r
++      "multiple paths per message", "rw" },\r
++    { NOTMUCH_FEATURE_DIRECTORY_DOCS,\r
++      "relative directory paths", "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/message-ID in database", "w"},\r
+-    {NOTMUCH_FEATURE_BOOL_FOLDER,            "exact folder:/path: search", "rw"},\r
++    { NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES,\r
++      "from/subject/message-ID in database", "w" },\r
++    { NOTMUCH_FEATURE_BOOL_FOLDER,\r
++      "exact folder:/path: search", "rw" },\r
+ };\r
\r
+ const char *\r
+@@ -658,6 +661,7 @@ _notmuch_database_ensure_writable (notmuch_database_t *notmuch)\r
+ }\r
\r
+ /* Parse a database features string from the given database version.\r
++ * Returns the feature bit set.\r
+  *\r
+  * For version < 3, this ignores the features string and returns a\r
+  * hard-coded set of features.\r
+@@ -665,13 +669,15 @@ _notmuch_database_ensure_writable (notmuch_database_t *notmuch)\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
++ * *incompat_out suitable for presenting to the user.  *incompat_out\r
++ * will be allocated from ctx.\r
+  */\r
+-static unsigned int\r
++static _notmuch_features\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
++    _notmuch_features res = static_cast<_notmuch_features>(0);\r
++    unsigned int namelen, i;\r
+     size_t llen = 0;\r
+     const char *flags;\r
\r
+@@ -699,7 +705,7 @@ _parse_features (const void *ctx, const char *features, unsigned int version,\r
+           }\r
+       }\r
\r
+-      if (i == ARRAY_SIZE (feature_names)) {\r
++      if (i == ARRAY_SIZE (feature_names) && incompat_out) {\r
+           /* Unrecognized feature */\r
+           const char *have = strchr (flags, mode);\r
+           if (have && have < features + llen) {\r
+@@ -1167,7 +1173,8 @@ notmuch_bool_t\r
+ notmuch_database_needs_upgrade (notmuch_database_t *notmuch)\r
+ {\r
+     return notmuch->mode == NOTMUCH_DATABASE_MODE_READ_WRITE &&\r
+-      (NOTMUCH_FEATURES_CURRENT & ~notmuch->features);\r
++      ((NOTMUCH_FEATURES_CURRENT & ~notmuch->features) ||\r
++       (notmuch_database_get_version (notmuch) < NOTMUCH_DATABASE_VERSION));\r
+ }\r
\r
+ static volatile sig_atomic_t do_progress_notify = 0;\r
+@@ -1202,7 +1209,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
+     struct sigaction action;\r
+     struct itimerval timerval;\r
+     notmuch_bool_t timer_is_active = FALSE;\r
+-    unsigned int target_features, new_features;\r
++    enum _notmuch_features target_features, new_features;\r
+     notmuch_status_t status;\r
+     unsigned int count = 0, total = 0;\r
\r
+diff --git a/lib/notmuch.h b/lib/notmuch.h\r
+index d771eb2..21a5225 100644\r
+--- a/lib/notmuch.h\r
++++ b/lib/notmuch.h\r
+@@ -352,12 +352,14 @@ unsigned int\r
+ notmuch_database_get_version (notmuch_database_t *database);\r
\r
+ /**\r
+- * Is the database behind the latest supported database version?\r
++ * Can the database be upgraded to a newer database version?\r
+  *\r
+  * If this function returns TRUE, then the caller may call\r
+  * notmuch_database_upgrade to upgrade the database.  If the caller\r
+  * does not upgrade an out-of-date database, then some functions may\r
+- * fail with NOTMUCH_STATUS_UPGRADE_REQUIRED.\r
++ * fail with NOTMUCH_STATUS_UPGRADE_REQUIRED.  This always returns\r
++ * FALSE for a read-only database because there's no way to upgrade a\r
++ * read-only database.\r
+  */\r
+ notmuch_bool_t\r
+ notmuch_database_needs_upgrade (notmuch_database_t *database);\r
+\r
+\r
+\r