[WIP patch 1/9] lib: initial API for prefixed metadata

David Bremner david at tethera.net
Sat Jan 9 18:51:33 PST 2016


Start with get and set of a single key
---
 lib/Makefile.local    |   1 +
 lib/metadata.cc       | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/notmuch.h         |  25 +++++++++
 test/T590-metadata.sh |  58 +++++++++++++++++++
 4 files changed, 234 insertions(+)
 create mode 100644 lib/metadata.cc
 create mode 100755 test/T590-metadata.sh

diff --git a/lib/Makefile.local b/lib/Makefile.local
index 3a07090..ccc1e49 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -48,6 +48,7 @@ libnotmuch_cxx_srcs =		\
 	$(dir)/index.cc		\
 	$(dir)/message.cc	\
 	$(dir)/query.cc		\
+	$(dir)/metadata.cc	\
 	$(dir)/thread.cc
 
 libnotmuch_modules := $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o)
diff --git a/lib/metadata.cc b/lib/metadata.cc
new file mode 100644
index 0000000..a068ed1
--- /dev/null
+++ b/lib/metadata.cc
@@ -0,0 +1,150 @@
+/* metadata.cc - API for database metadata
+ *
+ * Copyright © 2015 David Bremner
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: David Bremner <david at tethera.net>
+ */
+
+#include "notmuch.h"
+#include "notmuch-private.h"
+#include "database-private.h"
+
+static
+const char *
+_find_metadata_prefix (notmuch_metadata_class_t mclass)
+{
+    switch (mclass) {
+    case NOTMUCH_METADATA_CONFIG:
+	return "C";
+    default:
+	return NULL;
+    }
+}
+
+notmuch_status_t _make_key(void *ctx, notmuch_metadata_class_t mclass,
+			    const char *in, const char **out)
+{
+    const char *term;
+    const char *prefix = NULL;
+
+    if (!out)
+	return NOTMUCH_STATUS_NULL_POINTER;
+
+    prefix = _find_metadata_prefix(mclass);
+    if (!prefix)
+	return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
+
+    term = talloc_asprintf (ctx, "%s%s",
+			    prefix, in);
+    if (!term)
+	return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+    *out = term;
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
+notmuch_status_t
+notmuch_database_set_metadata (notmuch_database_t *notmuch,
+			       notmuch_metadata_class_t mclass,
+			       const char *key,
+			       const char *value)
+{
+    notmuch_status_t status;
+    Xapian::WritableDatabase *db;
+    const char *key_term = NULL;
+    void *local;
+
+    local = talloc_new (NULL);
+
+    status = _notmuch_database_ensure_writable (notmuch);
+    if (status)
+	goto DONE;
+
+    status = _make_key (local, mclass, key, &key_term);
+    if (status)
+	goto DONE;
+
+    try {
+	db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
+	db->set_metadata (key_term, value);
+    } catch (const Xapian::Error &error) {
+	status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+	notmuch->exception_reported = TRUE;
+	if (! notmuch->exception_reported) {
+	    _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n",
+				   error.get_msg().c_str());
+	}
+    }
+ DONE:
+    talloc_free (local);
+
+    return status;
+}
+
+static notmuch_status_t
+_metadata_value (notmuch_database_t *notmuch,
+		 notmuch_metadata_class_t mclass,
+		 const char *key,
+		 std::string &value)
+{
+    notmuch_status_t status;
+
+    const char *key_term = NULL;
+    void *local;
+
+    local = talloc_new (NULL);
+
+    status = _make_key (local, mclass, key, &key_term);
+    if (status)
+	goto DONE;
+
+    try {
+
+	value = notmuch->xapian_db->get_metadata (key_term);
+
+    } catch (const Xapian::Error &error) {
+	status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+	notmuch->exception_reported = TRUE;
+	if (! notmuch->exception_reported) {
+	    _notmuch_database_log (notmuch, "Error: A Xapian exception occurred getting metadata: %s\n",
+				   error.get_msg().c_str());
+	}
+    }
+
+ DONE:
+    talloc_free (local);
+    return status;
+}
+
+notmuch_status_t
+notmuch_database_get_metadata (notmuch_database_t *notmuch,
+			       notmuch_metadata_class_t mclass,
+			       const char *key,
+			       char **value) {
+    std::string strval;
+    notmuch_status_t status;
+
+    if (!value)
+	return NOTMUCH_STATUS_NULL_POINTER;
+
+    status = _metadata_value (notmuch, mclass, key, strval);
+    if (status)
+	return status;
+
+    *value = strdup (strval.c_str ());
+
+    return NOTMUCH_STATUS_SUCCESS;
+}
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 310a8b8..448f405 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -1829,6 +1829,31 @@ notmuch_filenames_move_to_next (notmuch_filenames_t *filenames);
 void
 notmuch_filenames_destroy (notmuch_filenames_t *filenames);
 
+/**
+ * metadata class
+ */
+
+typedef enum _notmuch_metadata_class {
+    NOTMUCH_METADATA_FIRST_CLASS = 1,
+    NOTMUCH_METADATA_CONFIG = 1,
+    NOTMUCH_METADATA_LAST_CLASS
+} notmuch_metadata_class_t;
+
+/**
+ * set metadata
+ *
+ */
+notmuch_status_t
+notmuch_database_set_metadata (notmuch_database_t *db, notmuch_metadata_class_t mclass, const char *key, const char *value);
+
+/**
+ * retrieve one metadata value
+ *
+ * return value is allocated by malloc and should be freed by the caller.
+ */
+notmuch_status_t
+notmuch_database_get_metadata (notmuch_database_t *db, notmuch_metadata_class_t mclass, const char *key, char **value);
+
 /* @} */
 
 NOTMUCH_END_DECLS
diff --git a/test/T590-metadata.sh b/test/T590-metadata.sh
new file mode 100755
index 0000000..29aeaa2
--- /dev/null
+++ b/test/T590-metadata.sh
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+test_description="metadata API"
+
+. ./test-lib.sh || exit 1
+
+add_email_corpus
+
+cat <<EOF > c_head
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <notmuch.h>
+
+void run(int line, notmuch_status_t ret)
+{
+   if (ret) {
+	fprintf (stderr, "line %d: %s\n", line, ret);
+	exit (1);
+   }
+}
+
+#define RUN(v)  run(__LINE__, v);
+
+int main (int argc, char** argv)
+{
+   notmuch_database_t *db;
+   char *val;
+   notmuch_status_t stat;
+
+   RUN(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db));
+
+EOF
+
+cat <<EOF > c_tail
+   RUN(notmuch_database_destroy(db));
+}
+EOF
+
+test_begin_subtest "notmuch_database_{set,get}_metadata"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+{
+   RUN(notmuch_database_set_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey1", "testvalue1"));
+   RUN(notmuch_database_set_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey2", "testvalue2"));
+   RUN(notmuch_database_get_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey1", &val));
+   printf("testkey1 = %s\n", val);
+   RUN(notmuch_database_get_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey2", &val));
+   printf("testkey2 = %s\n", val);
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+testkey1 = testvalue1
+testkey2 = testvalue2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
-- 
2.6.4



More information about the notmuch mailing list