Add notmuch_database_set_timestamp and notmuch_database_get_timestamp
authorCarl Worth <cworth@cworth.org>
Fri, 23 Oct 2009 21:31:01 +0000 (14:31 -0700)
committerCarl Worth <cworth@cworth.org>
Fri, 23 Oct 2009 21:31:01 +0000 (14:31 -0700)
These will be very helpful to implement an efficient "notmuch new"
command which imports new mail messages that have appeared.

database.cc
message.cc
notmuch-private.h
notmuch.c
notmuch.h

index 442b850d2264874442837fe2ec8e279d03fdc352..7858f9d45d790a6077d223f4966017f1957503fd 100644 (file)
@@ -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)
index ee0e8e1f80502f81aa74c7cfc3994866e852ef41..c5a6273fdea65bd2e3a5ee41f2e57bd1b6882efc 100644 (file)
@@ -82,7 +82,8 @@ prefix_t BOOLEAN_PREFIX[] = {
     { "attachment_extension", "O" },
     { "msgid", "Q" },
     { "thread", "H" },
-    { "ref", "R" }
+    { "ref", "R" },
+    { "timestamp", "KTS" },
 };
 
 const char *
index 5d0c1fae31edd36476802afa52f30b5c946dbece..88b01bd3029f0c0b39387a44f05d1376f2120a8f 100644 (file)
 #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>
index 9b841b3a5cc2470be60c06fcdafa3bab75a8b027..279d21a58013be7cff1e5547d42addd35034e3f8 100644 (file)
--- a/notmuch.c
+++ b/notmuch.c
  * 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>
index f568bc0a2da3458fad66b6b13f03d7fadb267f24..2c290fda85c8c6e97e2459b20ecfec46fa2b9058 100644 (file)
--- 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