--- /dev/null
+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