From 0ae9ac84473e122cd38b11fdf5028fa66bba7184 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 10 Jan 2016 22:51:33 +2000 Subject: [PATCH] [WIP patch 1/9] lib: initial API for prefixed metadata --- 19/6b16fc4f448fb9497cc0d257d3e8c8770715c8 | 330 ++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 19/6b16fc4f448fb9497cc0d257d3e8c8770715c8 diff --git a/19/6b16fc4f448fb9497cc0d257d3e8c8770715c8 b/19/6b16fc4f448fb9497cc0d257d3e8c8770715c8 new file mode 100644 index 000000000..ebff5c518 --- /dev/null +++ b/19/6b16fc4f448fb9497cc0d257d3e8c8770715c8 @@ -0,0 +1,330 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by arlo.cworth.org (Postfix) with ESMTP id A88E66DE1502 + for ; Sat, 9 Jan 2016 18:51:59 -0800 (PST) +X-Virus-Scanned: Debian amavisd-new at cworth.org +X-Spam-Flag: NO +X-Spam-Score: -0.312 +X-Spam-Level: +X-Spam-Status: No, score=-0.312 tagged_above=-999 required=5 tests=[AWL=0.239, + RP_MATCHES_RCVD=-0.55, SPF_PASS=-0.001] autolearn=disabled +Received: from arlo.cworth.org ([127.0.0.1]) + by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id tcLveYXDaGuQ for ; + Sat, 9 Jan 2016 18:51:56 -0800 (PST) +Received: from fethera.tethera.net (fethera.tethera.net [198.245.60.197]) + by arlo.cworth.org (Postfix) with ESMTPS id 5FBA36DE02CB + for ; Sat, 9 Jan 2016 18:51:54 -0800 (PST) +Received: from remotemail by fethera.tethera.net with local (Exim 4.84) + (envelope-from ) + id 1aI66a-0007DL-UC; Sat, 09 Jan 2016 21:51:44 -0500 +Received: (nullmailer pid 29638 invoked by uid 1000); + Sun, 10 Jan 2016 02:51:47 -0000 +From: David Bremner +To: notmuch@notmuchmail.org +Subject: [WIP patch 1/9] lib: initial API for prefixed metadata +Date: Sat, 9 Jan 2016 22:51:33 -0400 +Message-Id: <1452394301-29499-2-git-send-email-david@tethera.net> +X-Mailer: git-send-email 2.6.4 +In-Reply-To: <1452394301-29499-1-git-send-email-david@tethera.net> +References: <1452394301-29499-1-git-send-email-david@tethera.net> +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.20 +Precedence: list +List-Id: "Use and development of the notmuch mail system." + +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +X-List-Received-Date: Sun, 10 Jan 2016 02:51:59 -0000 + +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 ++ */ ++ ++#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 (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 < c_head ++#include ++#include ++#include ++#include ++ ++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 < 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 + -- 2.26.2