From 68a10091d6b2c29e996ee84040eecad487cb5e91 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Fri, 23 Oct 2009 14:31:01 -0700 Subject: [PATCH] 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. --- database.cc | 95 +++++++++++++++++++++++++++++++++++++++++++++++ message.cc | 3 +- notmuch-private.h | 10 ++--- notmuch.c | 6 +-- notmuch.h | 53 ++++++++++++++++++++++++++ 5 files changed, 158 insertions(+), 9 deletions(-) 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 +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* For getline */ +#endif +#include + #include "notmuch.h" NOTMUCH_BEGIN_DECLS -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* For getline */ -#endif - -#include #include #include #include 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 */ -#include "notmuch.h" - #ifndef _GNU_SOURCE #define _GNU_SOURCE /* for getline */ #endif +#include + +#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 #include #include #include 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 + #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