aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDavid Bremner <david@tethera.net>2015-12-13 08:39:11 -0400
committerDavid Bremner <david@tethera.net>2015-12-13 08:39:11 -0400
commite3cd357fdddd2d8d29e8eea0b40a1043c46f1791 (patch)
treeb0e4f0549fc1216439e4593b73206c7923995ad4 /lib
parentad5b7434978ed98b1eb832b77684869f5d02594a (diff)
parent1432a4f946e0f236179b53ac71d03764da725f33 (diff)
Merge tag 'debian/0.21-3' into jessie-backports
uploaded to unstable
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.local23
-rw-r--r--lib/database-private.h18
-rw-r--r--lib/database.cc107
-rw-r--r--lib/directory.cc25
-rw-r--r--lib/gen-version-script.sh1
-rw-r--r--lib/message-file.c21
-rw-r--r--lib/message.cc34
-rw-r--r--lib/notmuch-private.h11
-rw-r--r--lib/notmuch.h144
-rw-r--r--lib/parse-time-vrp.cc5
-rw-r--r--lib/query.cc53
-rw-r--r--lib/thread.cc7
12 files changed, 358 insertions, 91 deletions
diff --git a/lib/Makefile.local b/lib/Makefile.local
index f9ecd50e..3a070907 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -1,26 +1,5 @@
# -*- makefile -*-
-# The major version of the library interface. This will control the soname.
-# As such, this number must be incremented for any incompatible change to
-# the library interface, (such as the deletion of an API or a major
-# semantic change that breaks formerly functioning code).
-#
-LIBNOTMUCH_VERSION_MAJOR = 4
-
-# The minor version of the library interface. This should be incremented at
-# the time of release for any additions to the library interface,
-# (and when it is incremented, the release version of the library should
-# be reset to 0).
-LIBNOTMUCH_VERSION_MINOR = 2
-
-# The release version the library interface. This should be incremented at
-# the time of release if there have been no changes to the interface, (but
-# simply compatible changes to the implementation).
-LIBNOTMUCH_VERSION_RELEASE = 0
-
-# Note: Don't forget to change the VERSION macros in notmuch.h when
-# any of the above change.
-
ifeq ($(PLATFORM),MACOSX)
LIBRARY_SUFFIX = dylib
# On OS X, library version numbers go before suffix.
@@ -33,7 +12,7 @@ LIBRARY_SUFFIX = so
LINKER_NAME = libnotmuch.$(LIBRARY_SUFFIX)
SONAME = $(LINKER_NAME).$(LIBNOTMUCH_VERSION_MAJOR)
LIBNAME = $(SONAME).$(LIBNOTMUCH_VERSION_MINOR).$(LIBNOTMUCH_VERSION_RELEASE)
-LIBRARY_LINK_FLAG = -shared -Wl,--version-script=notmuch.sym,-soname=$(SONAME) -Wl,--no-undefined
+LIBRARY_LINK_FLAG = -shared -Wl,--version-script=notmuch.sym,-soname=$(SONAME) $(NO_UNDEFINED_LDFLAGS)
ifeq ($(PLATFORM),OPENBSD)
LIBRARY_LINK_FLAG += -lc
endif
diff --git a/lib/database-private.h b/lib/database-private.h
index 24243db2..3fb10f7a 100644
--- a/lib/database-private.h
+++ b/lib/database-private.h
@@ -100,6 +100,12 @@ enum _notmuch_features {
*
* Introduced: version 3. */
NOTMUCH_FEATURE_INDEXED_MIMETYPES = 1 << 5,
+
+ /* If set, messages store the revision number of the last
+ * modification in NOTMUCH_VALUE_LAST_MOD.
+ *
+ * Introduced: version 3. */
+ NOTMUCH_FEATURE_LAST_MOD = 1 << 6,
};
/* In C++, a named enum is its own type, so define bitwise operators
@@ -145,6 +151,8 @@ struct _notmuch_database {
notmuch_database_mode_t mode;
int atomic_nesting;
+ /* TRUE if changes have been made in this atomic section */
+ notmuch_bool_t atomic_dirty;
Xapian::Database *xapian_db;
/* Bit mask of features used by this database. This is a
@@ -158,10 +166,17 @@ struct _notmuch_database {
* next library call. May be NULL */
char *status_string;
+ /* Highest committed revision number. Modifications are recorded
+ * under a higher revision number, which can be generated with
+ * notmuch_database_new_revision. */
+ unsigned long revision;
+ const char *uuid;
+
Xapian::QueryParser *query_parser;
Xapian::TermGenerator *term_gen;
Xapian::ValueRangeProcessor *value_range_processor;
Xapian::ValueRangeProcessor *date_range_processor;
+ Xapian::ValueRangeProcessor *last_mod_range_processor;
};
/* Prior to database version 3, features were implied by the database
@@ -179,7 +194,8 @@ struct _notmuch_database {
* will have it). */
#define NOTMUCH_FEATURES_CURRENT \
(NOTMUCH_FEATURE_FILE_TERMS | NOTMUCH_FEATURE_DIRECTORY_DOCS | \
- NOTMUCH_FEATURE_BOOL_FOLDER | NOTMUCH_FEATURE_GHOSTS)
+ NOTMUCH_FEATURE_BOOL_FOLDER | NOTMUCH_FEATURE_GHOSTS | \
+ NOTMUCH_FEATURE_LAST_MOD)
/* Return the list of terms from the given iterator matching a prefix.
* The prefix will be stripped from the strings in the returned list.
diff --git a/lib/database.cc b/lib/database.cc
index cffab62c..5e86955d 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -101,6 +101,9 @@ typedef struct {
*
* SUBJECT: The value of the "Subject" header
*
+ * LAST_MOD: The revision number as of the last tag or
+ * filename change.
+ *
* In addition, terms from the content of the message are added with
* "from", "to", "attachment", and "subject" prefixes for use by the
* user in searching. Similarly, terms from the path of the mail
@@ -310,6 +313,8 @@ static const struct {
* them. */
{ NOTMUCH_FEATURE_INDEXED_MIMETYPES,
"indexed MIME types", "w"},
+ { NOTMUCH_FEATURE_LAST_MOD,
+ "modification tracking", "w"},
};
const char *
@@ -342,6 +347,8 @@ notmuch_status_to_string (notmuch_status_t status)
return "Unsupported operation";
case NOTMUCH_STATUS_UPGRADE_REQUIRED:
return "Operation requires a database upgrade";
+ case NOTMUCH_STATUS_PATH_ERROR:
+ return "Path supplied is illegal for this function";
default:
case NOTMUCH_STATUS_LAST_STATUS:
return "Unknown error status value";
@@ -657,6 +664,12 @@ notmuch_database_create_verbose (const char *path,
goto DONE;
}
+ if (path[0] != '/') {
+ message = strdup ("Error: Database path must be absolute.\n");
+ status = NOTMUCH_STATUS_PATH_ERROR;
+ goto DONE;
+ }
+
err = stat (path, &st);
if (err) {
IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: %s.\n",
@@ -729,6 +742,23 @@ _notmuch_database_ensure_writable (notmuch_database_t *notmuch)
return NOTMUCH_STATUS_SUCCESS;
}
+/* Allocate a revision number for the next change. */
+unsigned long
+_notmuch_database_new_revision (notmuch_database_t *notmuch)
+{
+ unsigned long new_revision = notmuch->revision + 1;
+
+ /* If we're in an atomic section, hold off on updating the
+ * committed revision number until we commit the atomic section.
+ */
+ if (notmuch->atomic_nesting)
+ notmuch->atomic_dirty = TRUE;
+ else
+ notmuch->revision = new_revision;
+
+ return new_revision;
+}
+
/* Parse a database features string from the given database version.
* Returns the feature bit set.
*
@@ -847,6 +877,12 @@ notmuch_database_open_verbose (const char *path,
goto DONE;
}
+ if (path[0] != '/') {
+ message = strdup ("Error: Database path must be absolute.\n");
+ status = NOTMUCH_STATUS_PATH_ERROR;
+ goto DONE;
+ }
+
if (! (notmuch_path = talloc_asprintf (local, "%s/%s", path, ".notmuch"))) {
message = strdup ("Out of memory\n");
status = NOTMUCH_STATUS_OUT_OF_MEMORY;
@@ -890,6 +926,7 @@ notmuch_database_open_verbose (const char *path,
notmuch->atomic_nesting = 0;
try {
string last_thread_id;
+ string last_mod;
if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {
notmuch->xapian_db = new Xapian::WritableDatabase (xapian_path,
@@ -948,11 +985,22 @@ notmuch_database_open_verbose (const char *path,
INTERNAL_ERROR ("Malformed database last_thread_id: %s", str);
}
+ /* Get current highest revision number. */
+ last_mod = notmuch->xapian_db->get_value_upper_bound (
+ NOTMUCH_VALUE_LAST_MOD);
+ if (last_mod.empty ())
+ notmuch->revision = 0;
+ else
+ notmuch->revision = Xapian::sortable_unserialise (last_mod);
+ notmuch->uuid = talloc_strdup (
+ notmuch, notmuch->xapian_db->get_uuid ().c_str ());
+
notmuch->query_parser = new Xapian::QueryParser;
notmuch->term_gen = new Xapian::TermGenerator;
notmuch->term_gen->set_stemmer (Xapian::Stem ("english"));
notmuch->value_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
notmuch->date_range_processor = new ParseTimeValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
+ notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
notmuch->query_parser->set_default_op (Xapian::Query::OP_AND);
notmuch->query_parser->set_database (*notmuch->xapian_db);
@@ -960,6 +1008,7 @@ notmuch_database_open_verbose (const char *path,
notmuch->query_parser->set_stemming_strategy (Xapian::QueryParser::STEM_SOME);
notmuch->query_parser->add_valuerangeprocessor (notmuch->value_range_processor);
notmuch->query_parser->add_valuerangeprocessor (notmuch->date_range_processor);
+ notmuch->query_parser->add_valuerangeprocessor (notmuch->last_mod_range_processor);
for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_EXTERNAL); i++) {
prefix_t *prefix = &BOOLEAN_PREFIX_EXTERNAL[i];
@@ -1038,6 +1087,8 @@ notmuch_database_close (notmuch_database_t *notmuch)
notmuch->value_range_processor = NULL;
delete notmuch->date_range_processor;
notmuch->date_range_processor = NULL;
+ delete notmuch->last_mod_range_processor;
+ notmuch->last_mod_range_processor = NULL;
return status;
}
@@ -1321,6 +1372,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
enum _notmuch_features target_features, new_features;
notmuch_status_t status;
notmuch_private_status_t private_status;
+ notmuch_query_t *query = NULL;
unsigned int count = 0, total = 0;
status = _notmuch_database_ensure_writable (notmuch);
@@ -1336,7 +1388,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
return NOTMUCH_STATUS_SUCCESS;
if (progress_notify) {
- /* Setup our handler for SIGALRM */
+ /* Set up our handler for SIGALRM */
memset (&action, 0, sizeof (struct sigaction));
action.sa_handler = handle_sigalrm;
sigemptyset (&action.sa_mask);
@@ -1355,10 +1407,18 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
/* Figure out how much total work we need to do. */
if (new_features &
- (NOTMUCH_FEATURE_FILE_TERMS | NOTMUCH_FEATURE_BOOL_FOLDER)) {
- notmuch_query_t *query = notmuch_query_create (notmuch, "");
- total += notmuch_query_count_messages (query);
+ (NOTMUCH_FEATURE_FILE_TERMS | NOTMUCH_FEATURE_BOOL_FOLDER |
+ NOTMUCH_FEATURE_LAST_MOD)) {
+ query = notmuch_query_create (notmuch, "");
+ unsigned msg_count;
+
+ status = notmuch_query_count_messages_st (query, &msg_count);
+ if (status)
+ goto DONE;
+
+ total += msg_count;
notmuch_query_destroy (query);
+ query = NULL;
}
if (new_features & NOTMUCH_FEATURE_DIRECTORY_DOCS) {
t_end = db->allterms_end ("XTIMESTAMP");
@@ -1382,13 +1442,18 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
/* Perform per-message upgrades. */
if (new_features &
- (NOTMUCH_FEATURE_FILE_TERMS | NOTMUCH_FEATURE_BOOL_FOLDER)) {
- notmuch_query_t *query = notmuch_query_create (notmuch, "");
+ (NOTMUCH_FEATURE_FILE_TERMS | NOTMUCH_FEATURE_BOOL_FOLDER |
+ NOTMUCH_FEATURE_LAST_MOD)) {
notmuch_messages_t *messages;
notmuch_message_t *message;
char *filename;
- for (messages = notmuch_query_search_messages (query);
+ query = notmuch_query_create (notmuch, "");
+
+ status = notmuch_query_search_messages_st (query, &messages);
+ if (status)
+ goto DONE;
+ for (;
notmuch_messages_valid (messages);
notmuch_messages_move_to_next (messages))
{
@@ -1419,6 +1484,14 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
if (new_features & NOTMUCH_FEATURE_BOOL_FOLDER)
_notmuch_message_upgrade_folder (message);
+ /* Prior to NOTMUCH_FEATURE_LAST_MOD, messages did not
+ * track modification revisions. Give all messages the
+ * next available revision; since we just started tracking
+ * revisions for this database, that will be 1.
+ */
+ if (new_features & NOTMUCH_FEATURE_LAST_MOD)
+ _notmuch_message_upgrade_last_mod (message);
+
_notmuch_message_sync (message);
notmuch_message_destroy (message);
@@ -1427,6 +1500,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
}
notmuch_query_destroy (query);
+ query = NULL;
}
/* Perform per-directory upgrades. */
@@ -1547,6 +1621,9 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
sigaction (SIGALRM, &action, NULL);
}
+ if (query)
+ notmuch_query_destroy (query);
+
talloc_free (local);
return status;
}
@@ -1601,11 +1678,25 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch)
return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
}
+ if (notmuch->atomic_dirty) {
+ ++notmuch->revision;
+ notmuch->atomic_dirty = FALSE;
+ }
+
DONE:
notmuch->atomic_nesting--;
return NOTMUCH_STATUS_SUCCESS;
}
+unsigned long
+notmuch_database_get_revision (notmuch_database_t *notmuch,
+ const char **uuid)
+{
+ if (uuid)
+ *uuid = notmuch->uuid;
+ return notmuch->revision;
+}
+
/* We allow the user to use arbitrarily long paths for directories. But
* we have a term-length limit. So if we exceed that, we'll use the
* SHA-1 of the path for the database term.
@@ -2576,7 +2667,7 @@ notmuch_database_get_all_tags (notmuch_database_t *db)
}
const char *
-notmuch_database_status_string (notmuch_database_t *notmuch)
+notmuch_database_status_string (const notmuch_database_t *notmuch)
{
return notmuch->status_string;
}
diff --git a/lib/directory.cc b/lib/directory.cc
index b836ea28..78637b3a 100644
--- a/lib/directory.cc
+++ b/lib/directory.cc
@@ -281,6 +281,31 @@ notmuch_directory_get_child_directories (notmuch_directory_t *directory)
return child_directories;
}
+notmuch_status_t
+notmuch_directory_delete (notmuch_directory_t *directory)
+{
+ notmuch_status_t status;
+ Xapian::WritableDatabase *db;
+
+ status = _notmuch_database_ensure_writable (directory->notmuch);
+ if (status)
+ return status;
+
+ try {
+ db = static_cast <Xapian::WritableDatabase *> (directory->notmuch->xapian_db);
+ db->delete_document (directory->document_id);
+ } catch (const Xapian::Error &error) {
+ _notmuch_database_log (directory->notmuch,
+ "A Xapian exception occurred deleting directory entry: %s.\n",
+ error.get_msg().c_str());
+ directory->notmuch->exception_reported = TRUE;
+ status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ }
+ notmuch_directory_destroy (directory);
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
void
notmuch_directory_destroy (notmuch_directory_t *directory)
{
diff --git a/lib/gen-version-script.sh b/lib/gen-version-script.sh
index 64a73749..84770011 100644
--- a/lib/gen-version-script.sh
+++ b/lib/gen-version-script.sh
@@ -1,3 +1,4 @@
+set -eu
# we go through a bit of work to get the unmangled names of the
# typeinfo symbols because of
diff --git a/lib/message-file.c b/lib/message-file.c
index 8ac96e8e..ee305202 100644
--- a/lib/message-file.c
+++ b/lib/message-file.c
@@ -38,27 +38,6 @@ struct _notmuch_message_file {
};
static int
-strcase_equal (const void *a, const void *b)
-{
- return strcasecmp (a, b) == 0;
-}
-
-static unsigned int
-strcase_hash (const void *ptr)
-{
- const char *s = ptr;
-
- /* This is the djb2 hash. */
- unsigned int hash = 5381;
- while (s && *s) {
- hash = ((hash << 5) + hash) + tolower (*s);
- s++;
- }
-
- return hash;
-}
-
-static int
_notmuch_message_file_destructor (notmuch_message_file_t *message)
{
if (message->headers)
diff --git a/lib/message.cc b/lib/message.cc
index 5bc7aff1..26b5e76e 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -43,6 +43,9 @@ struct visible _notmuch_message {
* if each flag has been initialized. */
unsigned long lazy_flags;
+ /* Message document modified since last sync */
+ notmuch_bool_t modified;
+
Xapian::Document doc;
Xapian::termcount termpos;
};
@@ -539,6 +542,7 @@ _notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)
try {
message->doc.remove_term ((*i));
+ message->modified = TRUE;
} catch (const Xapian::InvalidArgumentError) {
/* Ignore failure to remove non-existent term. */
}
@@ -793,6 +797,7 @@ void
_notmuch_message_clear_data (notmuch_message_t *message)
{
message->doc.set_data ("");
+ message->modified = TRUE;
}
static void
@@ -990,6 +995,17 @@ _notmuch_message_set_header_values (notmuch_message_t *message,
Xapian::sortable_serialise (time_value));
message->doc.add_value (NOTMUCH_VALUE_FROM, from);
message->doc.add_value (NOTMUCH_VALUE_SUBJECT, subject);
+ message->modified = TRUE;
+}
+
+/* Upgrade a message to support NOTMUCH_FEATURE_LAST_MOD. The caller
+ * must call _notmuch_message_sync. */
+void
+_notmuch_message_upgrade_last_mod (notmuch_message_t *message)
+{
+ /* _notmuch_message_sync will update the last modification
+ * revision; we just have to ask it to. */
+ message->modified = TRUE;
}
/* Synchronize changes made to message->doc out into the database. */
@@ -1001,8 +1017,24 @@ _notmuch_message_sync (notmuch_message_t *message)
if (message->notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY)
return;
+ if (! message->modified)
+ return;
+
+ /* Update the last modification of this message. */
+ if (message->notmuch->features & NOTMUCH_FEATURE_LAST_MOD)
+ /* sortable_serialise gives a reasonably compact encoding,
+ * which directly translates to reduced IO when scanning the
+ * value stream. Since it's built for doubles, we only get 53
+ * effective bits, but that's still enough for the database to
+ * last a few centuries at 1 million revisions per second. */
+ message->doc.add_value (NOTMUCH_VALUE_LAST_MOD,
+ Xapian::sortable_serialise (
+ _notmuch_database_new_revision (
+ message->notmuch)));
+
db = static_cast <Xapian::WritableDatabase *> (message->notmuch->xapian_db);
db->replace_document (message->doc_id, message->doc);
+ message->modified = FALSE;
}
/* Delete a message document from the database. */
@@ -1077,6 +1109,7 @@ _notmuch_message_add_term (notmuch_message_t *message,
return NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG;
message->doc.add_term (term, 0);
+ message->modified = TRUE;
talloc_free (term);
@@ -1145,6 +1178,7 @@ _notmuch_message_remove_term (notmuch_message_t *message,
try {
message->doc.remove_term (term);
+ message->modified = TRUE;
} catch (const Xapian::InvalidArgumentError) {
/* We'll let the philosopher's try to wrestle with the
* question of whether failing to remove that which was not
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index cc9ce12c..5dd4770e 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -50,6 +50,7 @@ NOTMUCH_BEGIN_DECLS
#include "xutil.h"
#include "error_util.h"
+#include "string-util.h"
#pragma GCC visibility push(hidden)
@@ -107,7 +108,8 @@ typedef enum {
NOTMUCH_VALUE_TIMESTAMP = 0,
NOTMUCH_VALUE_MESSAGE_ID,
NOTMUCH_VALUE_FROM,
- NOTMUCH_VALUE_SUBJECT
+ NOTMUCH_VALUE_SUBJECT,
+ NOTMUCH_VALUE_LAST_MOD,
} notmuch_value_t;
/* Xapian (with flint backend) complains if we provide a term longer
@@ -194,6 +196,9 @@ void
_notmuch_database_log (notmuch_database_t *notmuch,
const char *format, ...);
+unsigned long
+_notmuch_database_new_revision (notmuch_database_t *notmuch);
+
const char *
_notmuch_database_relative_path (notmuch_database_t *notmuch,
const char *path);
@@ -305,6 +310,10 @@ _notmuch_message_set_header_values (notmuch_message_t *message,
const char *date,
const char *from,
const char *subject);
+
+void
+_notmuch_message_upgrade_last_mod (notmuch_message_t *message);
+
void
_notmuch_message_sync (notmuch_message_t *message);
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 20c4e019..85b56bf1 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -56,9 +56,11 @@ NOTMUCH_BEGIN_DECLS
* version in Makefile.local.
*/
#define LIBNOTMUCH_MAJOR_VERSION 4
-#define LIBNOTMUCH_MINOR_VERSION 2
+#define LIBNOTMUCH_MINOR_VERSION 3
#define LIBNOTMUCH_MICRO_VERSION 0
+#define NOTMUCH_DEPRECATED(major,minor) \
+ __attribute__ ((deprecated ("function deprecated as of libnotmuch " #major "." #minor)))
#endif /* __DOXYGEN__ */
/**
@@ -164,6 +166,11 @@ typedef enum _notmuch_status {
*/
NOTMUCH_STATUS_UPGRADE_REQUIRED,
/**
+ * There is a problem with the proposed path, e.g. a relative path
+ * passed to a function expecting an absolute path.
+ */
+ NOTMUCH_STATUS_PATH_ERROR,
+ /**
* Not an actual status value. Just a way to find out how many
* valid status values there are.
*/
@@ -306,7 +313,7 @@ notmuch_database_open_verbose (const char *path,
*
*/
const char *
-notmuch_database_status_string (notmuch_database_t *notmuch);
+notmuch_database_status_string (const notmuch_database_t *notmuch);
/**
* Commit changes and close the given notmuch database.
@@ -461,6 +468,24 @@ notmuch_status_t
notmuch_database_end_atomic (notmuch_database_t *notmuch);
/**
+ * Return the committed database revision and UUID.
+ *
+ * The database revision number increases monotonically with each
+ * commit to the database. Hence, all messages and message changes
+ * committed to the database (that is, visible to readers) have a last
+ * modification revision <= the committed database revision. Any
+ * messages committed in the future will be assigned a modification
+ * revision > the committed database revision.
+ *
+ * The UUID is a NUL-terminated opaque string that uniquely identifies
+ * this database. Two revision numbers are only comparable if they
+ * have the same database UUID.
+ */
+unsigned long
+notmuch_database_get_revision (notmuch_database_t *notmuch,
+ const char **uuid);
+
+/**
* Retrieve a directory object from the database for 'path'.
*
* Here, 'path' should be a path relative to the path of 'database'
@@ -703,7 +728,13 @@ typedef enum {
* Return the query_string of this query. See notmuch_query_create.
*/
const char *
-notmuch_query_get_query_string (notmuch_query_t *query);
+notmuch_query_get_query_string (const notmuch_query_t *query);
+
+/**
+ * Return the notmuch database of this query. See notmuch_query_create.
+ */
+notmuch_database_t *
+notmuch_query_get_database (const notmuch_query_t *query);
/**
* Exclude values for notmuch_query_set_omit_excluded. The strange
@@ -760,7 +791,7 @@ notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort);
* notmuch_query_set_sort.
*/
notmuch_sort_t
-notmuch_query_get_sort (notmuch_query_t *query);
+notmuch_query_get_sort (const notmuch_query_t *query);
/**
* Add a tag that will be excluded from the query results by default.
@@ -807,20 +838,26 @@ notmuch_query_add_tag_exclude (notmuch_query_t *query, const char *tag);
* notmuch_threads_destroy function, but there's no good reason
* to call it if the query is about to be destroyed).
*
- * If a Xapian exception occurs this function will return NULL.
- * For better error reporting, use the _st variant.
- */
-notmuch_threads_t *
-notmuch_query_search_threads (notmuch_query_t *query);
-
-/**
- * Like notmuch_query_search_threads, but with a status return.
+ * @since libnotmuch 4.2 (notmuch 0.20)
*/
notmuch_status_t
notmuch_query_search_threads_st (notmuch_query_t *query,
notmuch_threads_t **out);
/**
+ * Like notmuch_query_search_threads_st, but without a status return.
+ *
+ * If a Xapian exception occurs this function will return NULL.
+ *
+ * @deprecated Deprecated as of libnotmuch 4.3 (notmuch 0.21). Please
+ * use notmuch_query_search_threads_st instead.
+ *
+ */
+NOTMUCH_DEPRECATED(4,3)
+notmuch_threads_t *
+notmuch_query_search_threads (notmuch_query_t *query);
+
+/**
* Execute a query for messages, returning a notmuch_messages_t object
* which can be used to iterate over the results. The returned
* messages object is owned by the query and as such, will only be
@@ -858,17 +895,24 @@ notmuch_query_search_threads_st (notmuch_query_t *query,
* reason to call it if the query is about to be destroyed).
*
* If a Xapian exception occurs this function will return NULL.
- * For better error reporting, use the _st variant.
- */
-notmuch_messages_t *
-notmuch_query_search_messages (notmuch_query_t *query);
-
-/**
- * Like notmuch_query_search_messages, but with a status return.
+ *
+ * @since libnotmuch 4.2 (notmuch 0.20)
*/
notmuch_status_t
notmuch_query_search_messages_st (notmuch_query_t *query,
notmuch_messages_t **out);
+/**
+ * Like notmuch_query_search_messages, but without a status return.
+ *
+ * If a Xapian exception occurs this function will return NULL.
+ *
+ * @deprecated Deprecated as of libnotmuch 4.3 (notmuch 0.21). Please use
+ * notmuch_query_search_messages_st instead.
+ *
+ */
+NOTMUCH_DEPRECATED(4,3)
+notmuch_messages_t *
+notmuch_query_search_messages (notmuch_query_t *query);
/**
* Destroy a notmuch_query_t along with any associated resources.
@@ -942,10 +986,28 @@ notmuch_threads_destroy (notmuch_threads_t *threads);
* This function performs a search and returns the number of matching
* messages.
*
- * If a Xapian exception occurs, this function may return 0 (after
- * printing a message).
+ * @returns
+ *
+ * NOTMUCH_STATUS_SUCCESS: query completed successfully.
+ *
+ * NOTMUCH_STATUS_XAPIAN_EXCEPTION: a Xapian exception occured. The
+ * value of *count is not defined.
+ *
+ * @since libnotmuch 4.3 (notmuch 0.21)
+ */
+notmuch_status_t
+notmuch_query_count_messages_st (notmuch_query_t *query, unsigned int *count);
+
+/**
+ * like notmuch_query_count_messages_st, but without a status return.
+ *
+ * May return 0 in the case of errors.
+ *
+ * @deprecated Deprecated since libnotmuch 4.3 (notmuch 0.21). Please
+ * use notmuch_query_count_messages_st instead.
*/
-unsigned
+NOTMUCH_DEPRECATED(4,3)
+unsigned int
notmuch_query_count_messages (notmuch_query_t *query);
/**
@@ -956,11 +1018,33 @@ notmuch_query_count_messages (notmuch_query_t *query);
* search.
*
* Note that this is a significantly heavier operation than
- * notmuch_query_count_messages().
+ * notmuch_query_count_messages{_st}().
*
- * If an error occurs, this function may return 0.
+ * @returns
+ *
+ * NOTMUCH_STATUS_OUT_OF_MEMORY: Memory allocation failed. The value
+ * of *count is not defined
+
+ * NOTMUCH_STATUS_SUCCESS: query completed successfully.
+ *
+ * NOTMUCH_STATUS_XAPIAN_EXCEPTION: a Xapian exception occured. The
+ * value of *count is not defined.
+ *
+ * @since libnotmuch 4.3 (notmuch 0.21)
+ */
+notmuch_status_t
+notmuch_query_count_threads_st (notmuch_query_t *query, unsigned *count);
+
+/**
+ * like notmuch_query_count_threads, but without a status return.
+ *
+ * May return 0 in case of errors.
+ *
+ * @deprecated Deprecated as of libnotmuch 4.3 (notmuch 0.21). Please
+ * use notmuch_query_count_threads_st instead.
*/
-unsigned
+NOTMUCH_DEPRECATED(4,3)
+unsigned int
notmuch_query_count_threads (notmuch_query_t *query);
/**
@@ -1678,6 +1762,16 @@ notmuch_filenames_t *
notmuch_directory_get_child_directories (notmuch_directory_t *directory);
/**
+ * Delete directory document from the database, and destroy the
+ * notmuch_directory_t object. Assumes any child directories and files
+ * have been deleted by the caller.
+ *
+ * @since libnotmuch 4.3 (notmuch 0.21)
+ */
+notmuch_status_t
+notmuch_directory_delete (notmuch_directory_t *directory);
+
+/**
* Destroy a notmuch_directory_t object.
*/
void
diff --git a/lib/parse-time-vrp.cc b/lib/parse-time-vrp.cc
index 33f07db3..03804cf5 100644
--- a/lib/parse-time-vrp.cc
+++ b/lib/parse-time-vrp.cc
@@ -31,6 +31,7 @@ Xapian::valueno
ParseTimeValueRangeProcessor::operator() (std::string &begin, std::string &end)
{
time_t t, now;
+ std::string b;
/* Require date: prefix in start of the range... */
if (STRNCMP_LITERAL (begin.c_str (), PREFIX))
@@ -38,6 +39,7 @@ ParseTimeValueRangeProcessor::operator() (std::string &begin, std::string &end)
/* ...and remove it. */
begin.erase (0, sizeof (PREFIX) - 1);
+ b = begin;
/* Use the same 'now' for begin and end. */
if (time (&now) == (time_t) -1)
@@ -51,6 +53,9 @@ ParseTimeValueRangeProcessor::operator() (std::string &begin, std::string &end)
}
if (!end.empty ()) {
+ if (end == "!" && ! b.empty ())
+ end = b;
+
if (parse_time_string (end.c_str (), &t, &now, PARSE_TIME_ROUND_UP_INCLUSIVE))
return Xapian::BAD_VALUENO;
diff --git a/lib/query.cc b/lib/query.cc
index 9cedb6a8..e627bfc2 100644
--- a/lib/query.cc
+++ b/lib/query.cc
@@ -98,7 +98,7 @@ notmuch_query_create (notmuch_database_t *notmuch,
}
const char *
-notmuch_query_get_query_string (notmuch_query_t *query)
+notmuch_query_get_query_string (const notmuch_query_t *query)
{
return query->query_string;
}
@@ -117,7 +117,7 @@ notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort)
}
notmuch_sort_t
-notmuch_query_get_sort (notmuch_query_t *query)
+notmuch_query_get_sort (const notmuch_query_t *query)
{
return query->sort;
}
@@ -541,9 +541,19 @@ notmuch_threads_destroy (notmuch_threads_t *threads)
talloc_free (threads);
}
-unsigned
+unsigned int
notmuch_query_count_messages (notmuch_query_t *query)
{
+ notmuch_status_t status;
+ unsigned int count;
+
+ status = notmuch_query_count_messages_st (query, &count);
+ return status ? 0 : count;
+}
+
+notmuch_status_t
+notmuch_query_count_messages_st (notmuch_query_t *query, unsigned *count_out)
+{
notmuch_database_t *notmuch = query->notmuch;
const char *query_string = query->query_string;
Xapian::doccount count = 0;
@@ -605,31 +615,44 @@ notmuch_query_count_messages (notmuch_query_t *query)
"Query string was: %s\n",
error.get_msg().c_str(),
query->query_string);
-
+ return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
}
- return count;
+ *count_out = count;
+ return NOTMUCH_STATUS_SUCCESS;
}
unsigned
notmuch_query_count_threads (notmuch_query_t *query)
{
+ notmuch_status_t status;
+ unsigned int count;
+
+ status = notmuch_query_count_threads_st (query, &count);
+ return status ? 0 : count;
+}
+
+notmuch_status_t
+notmuch_query_count_threads_st (notmuch_query_t *query, unsigned *count)
+{
notmuch_messages_t *messages;
GHashTable *hash;
- unsigned int count;
notmuch_sort_t sort;
+ notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
sort = query->sort;
query->sort = NOTMUCH_SORT_UNSORTED;
- messages = notmuch_query_search_messages (query);
+ ret = notmuch_query_search_messages_st (query, &messages);
+ if (ret)
+ return ret;
query->sort = sort;
if (messages == NULL)
- return 0;
+ return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
if (hash == NULL) {
talloc_free (messages);
- return 0;
+ return NOTMUCH_STATUS_OUT_OF_MEMORY;
}
while (notmuch_messages_valid (messages)) {
@@ -638,7 +661,7 @@ notmuch_query_count_threads (notmuch_query_t *query)
char *thread_id_copy = talloc_strdup (messages, thread_id);
if (unlikely (thread_id_copy == NULL)) {
notmuch_message_destroy (message);
- count = 0;
+ ret = NOTMUCH_STATUS_OUT_OF_MEMORY;
goto DONE;
}
g_hash_table_insert (hash, thread_id_copy, NULL);
@@ -646,11 +669,17 @@ notmuch_query_count_threads (notmuch_query_t *query)
notmuch_messages_move_to_next (messages);
}
- count = g_hash_table_size (hash);
+ *count = g_hash_table_size (hash);
DONE:
g_hash_table_unref (hash);
talloc_free (messages);
- return count;
+ return ret;
+}
+
+notmuch_database_t *
+notmuch_query_get_database (const notmuch_query_t *query)
+{
+ return query->notmuch;
}
diff --git a/lib/thread.cc b/lib/thread.cc
index 9847cf8b..0c937d76 100644
--- a/lib/thread.cc
+++ b/lib/thread.cc
@@ -447,6 +447,7 @@ _notmuch_thread_create (void *ctx,
notmuch_messages_t *messages;
notmuch_message_t *message;
+ notmuch_status_t status;
seed_message = _notmuch_message_create (local, notmuch, seed_doc_id, NULL);
if (! seed_message)
@@ -504,7 +505,11 @@ _notmuch_thread_create (void *ctx,
* oldest or newest subject is desired. */
notmuch_query_set_sort (thread_id_query, NOTMUCH_SORT_OLDEST_FIRST);
- for (messages = notmuch_query_search_messages (thread_id_query);
+ status = notmuch_query_search_messages_st (thread_id_query, &messages);
+ if (status)
+ goto DONE;
+
+ for (;
notmuch_messages_valid (messages);
notmuch_messages_move_to_next (messages))
{