[PATCH v4 00/11] Implement and use database "features"

Austin Clements amdragon at mit.edu
Mon Aug 25 10:25:58 PDT 2014


This is v4 of id:1406859003-11561-1-git-send-email-amdragon at mit.edu.
In addition to rebasing to current master, this makes several tidying
changes: it gives the features enum a name for better
self-documentation and type-safety; improves the robustness of
_parse_features to NULL pointers; requests upgrades if the database
version is old, even if it supports all current features; improves
various comments; and fixes some style errors.

The diff from v3 is below.  Most of this diff relates to giving the
enum a name, since it has to move above struct _notmuch_database and
we need to define bitwise operators for C++.

diff --git a/lib/database-private.h b/lib/database-private.h
index 2ffab33..ca0751c 100644
--- a/lib/database-private.h
+++ b/lib/database-private.h
@@ -36,36 +36,30 @@
 
 #pragma GCC visibility push(hidden)
 
-struct _notmuch_database {
-    notmuch_bool_t exception_reported;
-
-    char *path;
-
-    notmuch_database_mode_t mode;
-    int atomic_nesting;
-    Xapian::Database *xapian_db;
-
-    /* Bit mask of features used by this database.  Features are
-     * named, independent aspects of the database schema.  This is a
-     * bitwise-OR of NOTMUCH_FEATURE_* values (below). */
-    unsigned int features;
-
-    unsigned int last_doc_id;
-    uint64_t last_thread_id;
-
-    Xapian::QueryParser *query_parser;
-    Xapian::TermGenerator *term_gen;
-    Xapian::ValueRangeProcessor *value_range_processor;
-    Xapian::ValueRangeProcessor *date_range_processor;
-};
-
-/* Bit masks for _notmuch_database::features. */
-enum {
+/* Bit masks for _notmuch_database::features.  Features are named,
+ * independent aspects of the database schema.
+ *
+ * A database stores the set of features that it "uses" (implicitly
+ * before database version 3 and explicitly as of version 3).
+ *
+ * A given library version will "recognize" a particular set of
+ * features; if a database uses a feature that the library does not
+ * recognize, the library will refuse to open it.  It is assumed the
+ * set of recognized features grows monotonically over time.  A
+ * library version will "implement" some subset of the recognized
+ * features: some operations may require that the database use (or not
+ * use) some feature, while other operations may support both
+ * databases that use and that don't use some feature.
+ *
+ * On disk, the database stores string names for these features (see
+ * the feature_names array).  These enum bit values are never
+ * persisted to disk and may change freely.
+ */
+enum _notmuch_features {
     /* If set, file names are stored in "file-direntry" terms.  If
      * unset, file names are stored in document data.
      *
-     * Introduced: version 1.  Implementation support: both for read;
-     * required for write. */
+     * Introduced: version 1. */
     NOTMUCH_FEATURE_FILE_TERMS = 1 << 0,
 
     /* If set, directory timestamps are stored in documents with
@@ -73,7 +67,7 @@ enum {
      * timestamps are stored in documents with XTIMESTAMP terms and
      * absolute paths.
      *
-     * Introduced: version 1.  Implementation support: required. */
+     * Introduced: version 1. */
     NOTMUCH_FEATURE_DIRECTORY_DOCS = 1 << 1,
 
     /* If set, the from, subject, and message-id headers are stored in
@@ -82,7 +76,6 @@ enum {
      * retrieved from the message file.
      *
      * Introduced: optional in version 1, required as of version 3.
-     * Implementation support: both.
      */
     NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES = 1 << 2,
 
@@ -90,13 +83,71 @@ enum {
      * unset, folder terms are probabilistic and stemmed and path
      * terms do not exist.
      *
-     * Introduced: version 2.  Implementation support: required. */
+     * Introduced: version 2. */
     NOTMUCH_FEATURE_BOOL_FOLDER = 1 << 3,
 };
 
+/* In C++, a named enum is its own type, so define bitwise operators
+ * on _notmuch_features. */
+inline _notmuch_features
+operator|(_notmuch_features a, _notmuch_features b)
+{
+    return static_cast<_notmuch_features>(
+	static_cast<unsigned>(a) | static_cast<unsigned>(b));
+}
+
+inline _notmuch_features
+operator&(_notmuch_features a, _notmuch_features b)
+{
+    return static_cast<_notmuch_features>(
+	static_cast<unsigned>(a) & static_cast<unsigned>(b));
+}
+
+inline _notmuch_features
+operator~(_notmuch_features a)
+{
+    return static_cast<_notmuch_features>(~static_cast<unsigned>(a));
+}
+
+inline _notmuch_features&
+operator|=(_notmuch_features &a, _notmuch_features b)
+{
+    a = a | b;
+    return a;
+}
+
+inline _notmuch_features&
+operator&=(_notmuch_features &a, _notmuch_features b)
+{
+    a = a & b;
+    return a;
+}
+
+struct _notmuch_database {
+    notmuch_bool_t exception_reported;
+
+    char *path;
+
+    notmuch_database_mode_t mode;
+    int atomic_nesting;
+    Xapian::Database *xapian_db;
+
+    /* Bit mask of features used by this database.  This is a
+     * bitwise-OR of NOTMUCH_FEATURE_* values (above). */
+    enum _notmuch_features features;
+
+    unsigned int last_doc_id;
+    uint64_t last_thread_id;
+
+    Xapian::QueryParser *query_parser;
+    Xapian::TermGenerator *term_gen;
+    Xapian::ValueRangeProcessor *value_range_processor;
+    Xapian::ValueRangeProcessor *date_range_processor;
+};
+
 /* Prior to database version 3, features were implied by the database
  * version number, so hard-code them for earlier versions. */
-#define NOTMUCH_FEATURES_V0 (0)
+#define NOTMUCH_FEATURES_V0 ((enum _notmuch_features)0)
 #define NOTMUCH_FEATURES_V1 (NOTMUCH_FEATURES_V0 | NOTMUCH_FEATURE_FILE_TERMS | \
 			     NOTMUCH_FEATURE_DIRECTORY_DOCS)
 #define NOTMUCH_FEATURES_V2 (NOTMUCH_FEATURES_V1 | NOTMUCH_FEATURE_BOOL_FOLDER)
diff --git a/lib/database.cc b/lib/database.cc
index 63257c2..5116188 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -266,10 +266,9 @@ _find_prefix (const char *name)
     return "";
 }
 
-static const struct
-{
+static const struct {
     /* NOTMUCH_FEATURE_* value. */
-    unsigned int value;
+    _notmuch_features value;
     /* Feature name as it appears in the database.  This name should
      * be appropriate for displaying to the user if an older version
      * of notmuch doesn't support this feature. */
@@ -277,12 +276,16 @@ static const struct
     /* Compatibility flags when this feature is declared. */
     const char *flags;
 } feature_names[] = {
-    {NOTMUCH_FEATURE_FILE_TERMS,             "multiple paths per message", "rw"},
-    {NOTMUCH_FEATURE_DIRECTORY_DOCS,         "relative directory paths", "rw"},
+    { NOTMUCH_FEATURE_FILE_TERMS,
+      "multiple paths per message", "rw" },
+    { NOTMUCH_FEATURE_DIRECTORY_DOCS,
+      "relative directory paths", "rw" },
     /* Header values are not required for reading a database because a
      * reader can just refer to the message file. */
-    {NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES, "from/subject/message-ID in database", "w"},
-    {NOTMUCH_FEATURE_BOOL_FOLDER,            "exact folder:/path: search", "rw"},
+    { NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES,
+      "from/subject/message-ID in database", "w" },
+    { NOTMUCH_FEATURE_BOOL_FOLDER,
+      "exact folder:/path: search", "rw" },
 };
 
 const char *
@@ -658,6 +661,7 @@ _notmuch_database_ensure_writable (notmuch_database_t *notmuch)
 }
 
 /* Parse a database features string from the given database version.
+ * Returns the feature bit set.
  *
  * For version < 3, this ignores the features string and returns a
  * hard-coded set of features.
@@ -665,13 +669,15 @@ _notmuch_database_ensure_writable (notmuch_database_t *notmuch)
  * If there are unrecognized features that are required to open the
  * database in mode (which should be 'r' or 'w'), return a
  * comma-separated list of unrecognized but required features in
- * *incompat_out, which will be allocated from ctx.
+ * *incompat_out suitable for presenting to the user.  *incompat_out
+ * will be allocated from ctx.
  */
-static unsigned int
+static _notmuch_features
 _parse_features (const void *ctx, const char *features, unsigned int version,
 		 char mode, char **incompat_out)
 {
-    unsigned int res = 0, namelen, i;
+    _notmuch_features res = static_cast<_notmuch_features>(0);
+    unsigned int namelen, i;
     size_t llen = 0;
     const char *flags;
 
@@ -699,7 +705,7 @@ _parse_features (const void *ctx, const char *features, unsigned int version,
 	    }
 	}
 
-	if (i == ARRAY_SIZE (feature_names)) {
+	if (i == ARRAY_SIZE (feature_names) && incompat_out) {
 	    /* Unrecognized feature */
 	    const char *have = strchr (flags, mode);
 	    if (have && have < features + llen) {
@@ -1167,7 +1173,8 @@ notmuch_bool_t
 notmuch_database_needs_upgrade (notmuch_database_t *notmuch)
 {
     return notmuch->mode == NOTMUCH_DATABASE_MODE_READ_WRITE &&
-	(NOTMUCH_FEATURES_CURRENT & ~notmuch->features);
+	((NOTMUCH_FEATURES_CURRENT & ~notmuch->features) ||
+	 (notmuch_database_get_version (notmuch) < NOTMUCH_DATABASE_VERSION));
 }
 
 static volatile sig_atomic_t do_progress_notify = 0;
@@ -1202,7 +1209,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
     struct sigaction action;
     struct itimerval timerval;
     notmuch_bool_t timer_is_active = FALSE;
-    unsigned int target_features, new_features;
+    enum _notmuch_features target_features, new_features;
     notmuch_status_t status;
     unsigned int count = 0, total = 0;
 
diff --git a/lib/notmuch.h b/lib/notmuch.h
index d771eb2..21a5225 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -352,12 +352,14 @@ unsigned int
 notmuch_database_get_version (notmuch_database_t *database);
 
 /**
- * Is the database behind the latest supported database version?
+ * Can the database be upgraded to a newer database version?
  *
  * If this function returns TRUE, then the caller may call
  * notmuch_database_upgrade to upgrade the database.  If the caller
  * does not upgrade an out-of-date database, then some functions may
- * fail with NOTMUCH_STATUS_UPGRADE_REQUIRED.
+ * fail with NOTMUCH_STATUS_UPGRADE_REQUIRED.  This always returns
+ * FALSE for a read-only database because there's no way to upgrade a
+ * read-only database.
  */
 notmuch_bool_t
 notmuch_database_needs_upgrade (notmuch_database_t *database);





More information about the notmuch mailing list