From: Carl Worth <cworth@cworth.org>
Date: Fri, 23 Oct 2009 21:31:01 +0000 (-0700)
Subject: Add notmuch_database_set_timestamp and notmuch_database_get_timestamp
X-Git-Tag: 0.1~754
X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=68a10091d6b2c29e996ee84040eecad487cb5e91;p=notmuch-old

Add notmuch_database_set_timestamp and notmuch_database_get_timestamp

These will be very helpful to implement an efficient "notmuch new"
command which imports new mail messages that have appeared.
---

diff --git a/database.cc b/database.cc
index 442b850d..7858f9d4 100644
--- a/database.cc
+++ b/database.cc
@@ -484,6 +484,101 @@ notmuch_database_get_path (notmuch_database_t *notmuch)
     return notmuch->path;
 }
 
+notmuch_private_status_t
+find_timestamp_document (notmuch_database_t *notmuch, const char *db_key,
+			 Xapian::Document *doc, unsigned int *doc_id)
+{
+    return find_unique_document (notmuch, "timestamp", db_key, doc, doc_id);
+}
+
+/* We allow the user to use arbitrarily long keys for timestamps,
+ * (they're for filesystem paths after all, which have no limit we
+ * know about). But we have a term-length limit. So if we exceed that,
+ * we'll use the SHA-1 of the user's key as the actual key for
+ * constructing a database term.
+ *
+ * Caution: This function returns a newly allocated string which the
+ * caller should free() when finished.
+ */
+static char *
+timestamp_db_key (const char *key)
+{
+    if (strlen (key) + 1 > NOTMUCH_TERM_MAX) {
+	return notmuch_sha1_of_string (key);
+    } else {
+	return strdup (key);
+    }
+}
+
+notmuch_status_t
+notmuch_database_set_timestamp (notmuch_database_t *notmuch,
+				const char *key, time_t timestamp)
+{
+    Xapian::Document doc;
+    unsigned int doc_id;
+    notmuch_private_status_t status;
+    notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
+    char *db_key = NULL;
+
+    db_key = timestamp_db_key (key);
+
+    try {
+	status = find_timestamp_document (notmuch, db_key, &doc, &doc_id);
+
+	doc.add_value (0, Xapian::sortable_serialise (timestamp));
+
+	if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
+	    char *term = talloc_asprintf (NULL, "%s%s",
+					  _find_prefix ("timestamp"), db_key);
+	    doc.add_term (term);
+	    talloc_free (term);
+
+	    notmuch->xapian_db->add_document (doc);
+	} else {
+	    notmuch->xapian_db->replace_document (doc_id, doc);
+	}
+
+    } catch (Xapian::Error &error) {
+	fprintf (stderr, "A Xapian exception occurred: %s.\n",
+		 error.get_msg().c_str());
+	ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+    }
+
+    if (db_key)
+	free (db_key);
+
+    return ret;
+}
+
+time_t
+notmuch_database_get_timestamp (notmuch_database_t *notmuch, const char *key)
+{
+    Xapian::Document doc;
+    unsigned int doc_id;
+    notmuch_private_status_t status;
+    char *db_key = NULL;
+    time_t ret = 0;
+
+    db_key = timestamp_db_key (key);
+
+    try {
+	status = find_timestamp_document (notmuch, db_key, &doc, &doc_id);
+
+	if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND)
+	    goto DONE;
+
+	ret =  Xapian::sortable_unserialise (doc.get_value (0));
+    } catch (Xapian::Error &error) {
+	goto DONE;
+    }
+
+  DONE:
+    if (db_key)
+	free (db_key);
+
+    return ret;
+}
+
 notmuch_status_t
 notmuch_database_add_message (notmuch_database_t *notmuch,
 			      const char *filename)
diff --git a/message.cc b/message.cc
index ee0e8e1f..c5a6273f 100644
--- a/message.cc
+++ b/message.cc
@@ -82,7 +82,8 @@ prefix_t BOOLEAN_PREFIX[] = {
     { "attachment_extension", "O" },
     { "msgid", "Q" },
     { "thread", "H" },
-    { "ref", "R" }
+    { "ref", "R" },
+    { "timestamp", "KTS" },
 };
 
 const char *
diff --git a/notmuch-private.h b/notmuch-private.h
index 5d0c1fae..88b01bd3 100644
--- a/notmuch-private.h
+++ b/notmuch-private.h
@@ -21,15 +21,15 @@
 #ifndef NOTMUCH_PRIVATE_H
 #define NOTMUCH_PRIVATE_H
 
-#include "notmuch.h"
-
-NOTMUCH_BEGIN_DECLS
-
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE /* For getline */
 #endif
-
 #include <stdio.h>
+
+#include "notmuch.h"
+
+NOTMUCH_BEGIN_DECLS
+
 #include <stdlib.h>
 #include <stdarg.h>
 #include <sys/types.h>
diff --git a/notmuch.c b/notmuch.c
index 9b841b3a..279d21a5 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -18,11 +18,12 @@
  * Author: Carl Worth <cworth@cworth.org>
  */
 
-#include "notmuch.h"
-
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE /* for getline */
 #endif
+#include <stdio.h>
+
+#include "notmuch.h"
 
 /* This is separate from notmuch-private.h because we're trying to
  * keep notmuch.c from looking into any internals, (which helps us
@@ -30,7 +31,6 @@
  */
 #include "xutil.h"
 
-#include <stdio.h>
 #include <stddef.h>
 #include <string.h>
 #include <sys/stat.h>
diff --git a/notmuch.h b/notmuch.h
index f568bc0a..2c290fda 100644
--- a/notmuch.h
+++ b/notmuch.h
@@ -31,6 +31,8 @@
 
 NOTMUCH_BEGIN_DECLS
 
+#include <time.h>
+
 #ifndef FALSE
 #define FALSE 0
 #endif
@@ -172,6 +174,57 @@ notmuch_database_default_path (void);
 const char *
 notmuch_database_get_path (notmuch_database_t *database);
 
+/* Store a timestamp within the database.
+ *
+ * The Notmuch database will not interpret this key nor the timestamp
+ * values at all. It will merely store them together and return the
+ * timestamp when notmuch_database_get_timestamp is called with the
+ * same value for 'key'.
+ *
+ * The intention is for the caller to use the timestamp to allow
+ * efficient identification of new messages to be added to the
+ * database. The recommended usage is as follows:
+ *
+ *   o Read the mtime of a directory from the filesystem
+ *
+ *   o Call add_message for all mail files in the directory
+ *
+ *   o Call notmuch_database_set_timestamp with the path of the
+ *     directory as 'key' and the originally read mtime as 'value'.
+ *
+ * Then, when wanting to check for updates to the directory in the
+ * future, the client can call notmuch_database_get_timestamp and know
+ * that it only needs to add files if the mtime of the directory and
+ * files are newer than the stored timestamp.
+ *
+ * Note: The notmuch_database_get_timestamp function does not allow
+ * the caller to distinguish a timestamp of 0 from a non-existent
+ * timestamp. So don't store a timestamp of 0 unless you are
+ * comfortable with that.
+ *
+ * Return value:
+ *
+ * NOTMUCH_STATUS_SUCCESS: Timestamp successfully stored in database.
+ *
+ * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception
+ *	occurred. Timestamp not stored.
+ */
+notmuch_status_t
+notmuch_database_set_timestamp (notmuch_database_t *database,
+				const char *key, time_t timestamp);
+
+/* Retrieve a timestamp from the database.
+ *
+ * Returns the timestamp value previously stored by calling
+ * notmuch_database_set_timestamp with the same value for 'key'.
+ *
+ * Returns 0 if no timestamp is stored for 'key' or if any error
+ * occurred querying the database.
+ */
+time_t
+notmuch_database_get_timestamp (notmuch_database_t *database,
+				const char *key);
+
 /* Add a new message to the given notmuch database.
  *
  * Here,'filename' should be a path relative to the the path of