From 3b76adf9e2c026dd03b820f4c6eab50e25444113 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Sat, 14 Jan 2012 19:17:33 -0500 Subject: lib: Add support for automatically excluding tags from queries This is useful for tags like "deleted" and "spam" that people generally want to exclude from query results. These exclusions will be overridden if a tag is explicitly mentioned in a query. --- lib/notmuch.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib/notmuch.h') diff --git a/lib/notmuch.h b/lib/notmuch.h index 9f23a10..7929fe7 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -457,6 +457,12 @@ notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort); notmuch_sort_t notmuch_query_get_sort (notmuch_query_t *query); +/* Add a tag that will be excluded from the query results by default. + * This exclusion will be overridden if this tag appears explicitly in + * the query. */ +void +notmuch_query_add_tag_exclude (notmuch_query_t *query, const char *tag); + /* Execute a query for threads, returning a notmuch_threads_t object * which can be used to iterate over the results. The returned threads * object is owned by the query and as such, will only be valid until -- cgit v1.2.3 From c9eb94d7fb520612374870dda9b9058a85c9b03d Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Thu, 1 Mar 2012 22:30:37 +0000 Subject: lib: Make notmuch_query_search_messages set the exclude flag Add a flag NOTMUCH_MESSAGE_FLAG_EXCLUDED which is set by notmuch_query_search_messages for excluded messages. Also add an option omit_excluded_messages to the search that we do not want the excludes at all. This exclude flag will be added to notmuch_query_search threads in the next patch. --- lib/notmuch-private.h | 1 + lib/notmuch.h | 10 ++++++++- lib/query.cc | 59 +++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 63 insertions(+), 7 deletions(-) (limited to 'lib/notmuch.h') diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 7bf153e..e791bb0 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -401,6 +401,7 @@ typedef struct _notmuch_message_list { */ struct visible _notmuch_messages { notmuch_bool_t is_of_list_type; + notmuch_doc_id_set_t *excluded_doc_ids; notmuch_message_node_t *iterator; }; diff --git a/lib/notmuch.h b/lib/notmuch.h index 7929fe7..f75afae 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -449,6 +449,13 @@ typedef enum { const char * notmuch_query_get_query_string (notmuch_query_t *query); +/* Specify whether to results should omit the excluded results rather + * than just marking them excluded. This is useful for passing a + * notmuch_messages_t not containing the excluded messages to other + * functions. */ +void +notmuch_query_set_omit_excluded_messages (notmuch_query_t *query, notmuch_bool_t omit); + /* Specify the sorting desired for this query. */ void notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort); @@ -895,7 +902,8 @@ notmuch_message_get_filenames (notmuch_message_t *message); /* Message flags */ typedef enum _notmuch_message_flag { - NOTMUCH_MESSAGE_FLAG_MATCH + NOTMUCH_MESSAGE_FLAG_MATCH, + NOTMUCH_MESSAGE_FLAG_EXCLUDED } notmuch_message_flag_t; /* Get a value of a flag for the email corresponding to 'message'. */ diff --git a/lib/query.cc b/lib/query.cc index c25b301..ef2a11f 100644 --- a/lib/query.cc +++ b/lib/query.cc @@ -28,6 +28,7 @@ struct _notmuch_query { const char *query_string; notmuch_sort_t sort; notmuch_string_list_t *exclude_terms; + notmuch_bool_t omit_excluded_messages; }; typedef struct _notmuch_mset_messages { @@ -57,6 +58,12 @@ struct visible _notmuch_threads { notmuch_doc_id_set_t match_set; }; +/* We need this in the message functions so forward declare. */ +static notmuch_bool_t +_notmuch_doc_id_set_init (void *ctx, + notmuch_doc_id_set_t *doc_ids, + GArray *arr); + notmuch_query_t * notmuch_query_create (notmuch_database_t *notmuch, const char *query_string) @@ -79,6 +86,8 @@ notmuch_query_create (notmuch_database_t *notmuch, query->exclude_terms = _notmuch_string_list_create (query); + query->omit_excluded_messages = FALSE; + return query; } @@ -88,6 +97,12 @@ notmuch_query_get_query_string (notmuch_query_t *query) return query->query_string; } +void +notmuch_query_set_omit_excluded_messages (notmuch_query_t *query, notmuch_bool_t omit) +{ + query->omit_excluded_messages = omit; +} + void notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort) { @@ -124,8 +139,9 @@ _notmuch_messages_destructor (notmuch_mset_messages_t *messages) /* Return a query that matches messages with the excluded tags * registered with query. Any tags that explicitly appear in xquery - * will not be excluded. The caller of this function has to combine - * the returned query appropriately.*/ + * will not be excluded, and will be removed from the list of exclude + * tags. The caller of this function has to combine the returned + * query appropriately.*/ static Xapian::Query _notmuch_exclude_tags (notmuch_query_t *query, Xapian::Query xquery) { @@ -142,6 +158,8 @@ _notmuch_exclude_tags (notmuch_query_t *query, Xapian::Query xquery) if (it == end) exclude_query = Xapian::Query (Xapian::Query::OP_OR, exclude_query, Xapian::Query (term->string)); + else + term->string = talloc_strdup (query, ""); } return exclude_query; } @@ -173,6 +191,7 @@ notmuch_query_search_messages (notmuch_query_t *query) "mail")); Xapian::Query string_query, final_query, exclude_query; Xapian::MSet mset; + Xapian::MSetIterator iterator; unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN | Xapian::QueryParser::FLAG_PHRASE | Xapian::QueryParser::FLAG_LOVEHATE | @@ -190,11 +209,35 @@ notmuch_query_search_messages (notmuch_query_t *query) final_query = Xapian::Query (Xapian::Query::OP_AND, mail_query, string_query); } + messages->base.excluded_doc_ids = NULL; + + if (query->exclude_terms) { + exclude_query = _notmuch_exclude_tags (query, final_query); + exclude_query = Xapian::Query (Xapian::Query::OP_AND, + exclude_query, final_query); + + if (query->omit_excluded_messages) + final_query = Xapian::Query (Xapian::Query::OP_AND_NOT, + final_query, exclude_query); + else { + enquire.set_weighting_scheme (Xapian::BoolWeight()); + enquire.set_query (exclude_query); + + mset = enquire.get_mset (0, notmuch->xapian_db->get_doccount ()); + + GArray *excluded_doc_ids = g_array_new (FALSE, FALSE, sizeof (unsigned int)); + + for (iterator = mset.begin (); iterator != mset.end (); iterator++) { + unsigned int doc_id = *iterator; + g_array_append_val (excluded_doc_ids, doc_id); + } + messages->base.excluded_doc_ids = talloc (messages, _notmuch_doc_id_set); + _notmuch_doc_id_set_init (query, messages->base.excluded_doc_ids, + excluded_doc_ids); + g_array_unref (excluded_doc_ids); + } + } - exclude_query = _notmuch_exclude_tags (query, final_query); - - final_query = Xapian::Query (Xapian::Query::OP_AND_NOT, - final_query, exclude_query); enquire.set_weighting_scheme (Xapian::BoolWeight()); @@ -283,6 +326,10 @@ _notmuch_mset_messages_get (notmuch_messages_t *messages) INTERNAL_ERROR ("a messages iterator contains a non-existent document ID.\n"); } + if (messages->excluded_doc_ids && + _notmuch_doc_id_set_contains (messages->excluded_doc_ids, doc_id)) + notmuch_message_set_flag (message, NOTMUCH_MESSAGE_FLAG_EXCLUDED, TRUE); + return message; } -- cgit v1.2.3 From 1a53f9f116fa7c460cda3df532be921baaafb082 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Thu, 1 Mar 2012 22:30:38 +0000 Subject: lib: Add the exclude flag to notmuch_query_search_threads Add the NOTMUCH_MESSAGE_FLAG_EXCLUDED flag to notmuch_query_search_threads. Implemented by inspecting the tags directly in _notmuch_thread_create/_thread_add_message rather than as a Xapian query for speed reasons. Note notmuch_thread_get_matched_messages now returns the number of non-excluded matching messages. This API is not totally desirable but fixing it means breaking binary compatibility so we delay that. --- lib/notmuch-private.h | 7 +++++-- lib/notmuch.h | 6 ++++-- lib/query.cc | 1 + lib/thread.cc | 18 +++++++++++++++--- 4 files changed, 25 insertions(+), 7 deletions(-) (limited to 'lib/notmuch.h') diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index e791bb0..ea836f7 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -148,6 +148,8 @@ typedef enum _notmuch_private_status { typedef struct _notmuch_doc_id_set notmuch_doc_id_set_t; +typedef struct _notmuch_string_list notmuch_string_list_t; + /* database.cc */ /* Lookup a prefix value by name. @@ -216,6 +218,7 @@ _notmuch_thread_create (void *ctx, notmuch_database_t *notmuch, unsigned int seed_doc_id, notmuch_doc_id_set_t *match_set, + notmuch_string_list_t *excluded_terms, notmuch_sort_t sort); /* message.cc */ @@ -459,11 +462,11 @@ typedef struct _notmuch_string_node { struct _notmuch_string_node *next; } notmuch_string_node_t; -typedef struct visible _notmuch_string_list { +struct visible _notmuch_string_list { int length; notmuch_string_node_t *head; notmuch_string_node_t **tail; -} notmuch_string_list_t; +}; notmuch_string_list_t * _notmuch_string_list_create (const void *ctx); diff --git a/lib/notmuch.h b/lib/notmuch.h index f75afae..babd208 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -672,8 +672,10 @@ notmuch_thread_get_toplevel_messages (notmuch_thread_t *thread); /* Get the number of messages in 'thread' that matched the search. * * This count includes only the messages in this thread that were - * matched by the search from which the thread was created. Contrast - * with notmuch_thread_get_total_messages() . + * matched by the search from which the thread was created and were + * not excluded by any exclude tags passed in with the query (see + * notmuch_query_add_tag_exclude). Contrast with + * notmuch_thread_get_total_messages() . */ int notmuch_thread_get_matched_messages (notmuch_thread_t *thread); diff --git a/lib/query.cc b/lib/query.cc index ef2a11f..ab18fbc 100644 --- a/lib/query.cc +++ b/lib/query.cc @@ -475,6 +475,7 @@ notmuch_threads_get (notmuch_threads_t *threads) threads->query->notmuch, doc_id, &threads->match_set, + threads->query->exclude_terms, threads->query->sort); } diff --git a/lib/thread.cc b/lib/thread.cc index 0435ee6..e976d64 100644 --- a/lib/thread.cc +++ b/lib/thread.cc @@ -214,7 +214,8 @@ _thread_cleanup_author (notmuch_thread_t *thread, */ static void _thread_add_message (notmuch_thread_t *thread, - notmuch_message_t *message) + notmuch_message_t *message, + notmuch_string_list_t *exclude_terms) { notmuch_tags_t *tags; const char *tag; @@ -262,6 +263,15 @@ _thread_add_message (notmuch_thread_t *thread, notmuch_tags_move_to_next (tags)) { tag = notmuch_tags_get (tags); + /* Mark excluded messages. */ + for (notmuch_string_node_t *term = exclude_terms->head; term; + term = term->next) { + /* We ignore initial 'K'. */ + if (strcmp(tag, (term->string + 1)) == 0) { + notmuch_message_set_flag (message, NOTMUCH_MESSAGE_FLAG_EXCLUDED, TRUE); + break; + } + } g_hash_table_insert (thread->tags, xstrdup (tag), NULL); } } @@ -321,7 +331,8 @@ _thread_add_matched_message (notmuch_thread_t *thread, _thread_set_subject_from_message (thread, message); } - thread->matched_messages++; + if (!notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_EXCLUDED)) + thread->matched_messages++; if (g_hash_table_lookup_extended (thread->message_hash, notmuch_message_get_message_id (message), NULL, @@ -392,6 +403,7 @@ _notmuch_thread_create (void *ctx, notmuch_database_t *notmuch, unsigned int seed_doc_id, notmuch_doc_id_set_t *match_set, + notmuch_string_list_t *exclude_terms, notmuch_sort_t sort) { notmuch_thread_t *thread; @@ -467,7 +479,7 @@ _notmuch_thread_create (void *ctx, if (doc_id == seed_doc_id) message = seed_message; - _thread_add_message (thread, message); + _thread_add_message (thread, message, exclude_terms); if ( _notmuch_doc_id_set_contains (match_set, doc_id)) { _notmuch_doc_id_set_remove (match_set, doc_id); -- cgit v1.2.3 From d6fbef4690968c8d0b8e9dc19a903e693cace316 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 7 Apr 2012 17:10:03 +0100 Subject: lib: change default for notmuch_query_set_omit_excluded --- lib/notmuch.h | 23 ++++++++++++++++++----- lib/query.cc | 10 +++++----- 2 files changed, 23 insertions(+), 10 deletions(-) (limited to 'lib/notmuch.h') diff --git a/lib/notmuch.h b/lib/notmuch.h index babd208..673c423 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -449,12 +449,25 @@ typedef enum { const char * notmuch_query_get_query_string (notmuch_query_t *query); -/* Specify whether to results should omit the excluded results rather - * than just marking them excluded. This is useful for passing a - * notmuch_messages_t not containing the excluded messages to other - * functions. */ +/* Specify whether to omit excluded results or simply flag them. By + * default, this is set to TRUE. + * + * If this is TRUE, notmuch_query_search_messages will omit excluded + * messages from the results. notmuch_query_search_threads will omit + * threads that match only in excluded messages, but will include all + * messages in threads that match in at least one non-excluded + * message. + * + * The performance difference when calling + * notmuch_query_search_messages should be relatively small (and both + * should be very fast). However, in some cases, + * notmuch_query_search_threads is very much faster when omitting + * excluded messages as it does not need to construct the threads that + * only match in excluded messages. + */ + void -notmuch_query_set_omit_excluded_messages (notmuch_query_t *query, notmuch_bool_t omit); +notmuch_query_set_omit_excluded (notmuch_query_t *query, notmuch_bool_t omit_excluded); /* Specify the sorting desired for this query. */ void diff --git a/lib/query.cc b/lib/query.cc index 68ac1e4..e9c1a2d 100644 --- a/lib/query.cc +++ b/lib/query.cc @@ -28,7 +28,7 @@ struct _notmuch_query { const char *query_string; notmuch_sort_t sort; notmuch_string_list_t *exclude_terms; - notmuch_bool_t omit_excluded_messages; + notmuch_bool_t omit_excluded; }; typedef struct _notmuch_mset_messages { @@ -92,7 +92,7 @@ notmuch_query_create (notmuch_database_t *notmuch, query->exclude_terms = _notmuch_string_list_create (query); - query->omit_excluded_messages = FALSE; + query->omit_excluded = TRUE; return query; } @@ -104,9 +104,9 @@ notmuch_query_get_query_string (notmuch_query_t *query) } void -notmuch_query_set_omit_excluded_messages (notmuch_query_t *query, notmuch_bool_t omit) +notmuch_query_set_omit_excluded (notmuch_query_t *query, notmuch_bool_t omit_excluded) { - query->omit_excluded_messages = omit; + query->omit_excluded = omit_excluded; } void @@ -220,7 +220,7 @@ notmuch_query_search_messages (notmuch_query_t *query) if (query->exclude_terms) { exclude_query = _notmuch_exclude_tags (query, final_query); - if (query->omit_excluded_messages) + if (query->omit_excluded) final_query = Xapian::Query (Xapian::Query::OP_AND_NOT, final_query, exclude_query); else { -- cgit v1.2.3 From 7864350c938944276c1a378539da7670c211b9b5 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 25 Apr 2012 15:20:16 +0200 Subject: Split notmuch_database_close into two functions Formerly notmuch_database_close closed the xapian database and destroyed the talloc structure associated with the notmuch database object. Split notmuch_database_close into notmuch_database_close and notmuch_database_destroy. This makes it possible for long running programs to close the xapian database and thus release the lock associated with it without destroying the data structures obtained from it. This also makes the api more consistent since every other data structure has a destructor function. The comments in notmuch.h are a courtesy of Austin Clements. Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de> --- lib/database.cc | 14 ++++++++++++-- lib/notmuch.h | 22 ++++++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) (limited to 'lib/notmuch.h') diff --git a/lib/database.cc b/lib/database.cc index 16c4354..2fefcad 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -642,7 +642,7 @@ notmuch_database_open (const char *path, " read-write mode.\n", notmuch_path, version, NOTMUCH_DATABASE_VERSION); notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY; - notmuch_database_close (notmuch); + notmuch_database_destroy (notmuch); notmuch = NULL; goto DONE; } @@ -702,7 +702,7 @@ notmuch_database_open (const char *path, } catch (const Xapian::Error &error) { fprintf (stderr, "A Xapian exception occurred opening database: %s\n", error.get_msg().c_str()); - notmuch_database_close (notmuch); + notmuch_database_destroy (notmuch); notmuch = NULL; } @@ -738,9 +738,19 @@ notmuch_database_close (notmuch_database_t *notmuch) } delete notmuch->term_gen; + notmuch->term_gen = NULL; delete notmuch->query_parser; + notmuch->query_parser = NULL; delete notmuch->xapian_db; + notmuch->xapian_db = NULL; delete notmuch->value_range_processor; + notmuch->value_range_processor = NULL; +} + +void +notmuch_database_destroy (notmuch_database_t *notmuch) +{ + notmuch_database_close (notmuch); talloc_free (notmuch); } diff --git a/lib/notmuch.h b/lib/notmuch.h index 673c423..7d9e092 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -133,7 +133,7 @@ typedef struct _notmuch_filenames notmuch_filenames_t; * * After a successful call to notmuch_database_create, the returned * database will be open so the caller should call - * notmuch_database_close when finished with it. + * notmuch_database_destroy when finished with it. * * The database will not yet have any data in it * (notmuch_database_create itself is a very cheap function). Messages @@ -165,7 +165,7 @@ typedef enum { * An existing notmuch database can be identified by the presence of a * directory named ".notmuch" below 'path'. * - * The caller should call notmuch_database_close when finished with + * The caller should call notmuch_database_destroy when finished with * this database. * * In case of any failure, this function returns NULL, (after printing @@ -175,11 +175,25 @@ notmuch_database_t * notmuch_database_open (const char *path, notmuch_database_mode_t mode); -/* Close the given notmuch database, freeing all associated - * resources. See notmuch_database_open. */ +/* Close the given notmuch database. + * + * After notmuch_database_close has been called, calls to other + * functions on objects derived from this database may either behave + * as if the database had not been closed (e.g., if the required data + * has been cached) or may fail with a + * NOTMUCH_STATUS_XAPIAN_EXCEPTION. + * + * notmuch_database_close can be called multiple times. Later calls + * have no effect. + */ void notmuch_database_close (notmuch_database_t *database); +/* Destroy the notmuch database, closing it if necessary and freeing +* all associated resources. */ +void +notmuch_database_destroy (notmuch_database_t *database); + /* Return the database path of the given database. * * The return value is a string owned by notmuch so should not be -- cgit v1.2.3 From 5fddc07dc31481453c1af186bf7da241c00cdbf1 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 30 Apr 2012 12:25:33 -0400 Subject: lib/cli: Make notmuch_database_open return a status code It has been a long-standing issue that notmuch_database_open doesn't return any indication of why it failed. This patch changes its prototype to return a notmuch_status_t and set an out-argument to the database itself, like other functions that return both a status and an object. In the interest of atomicity, this also updates every use in the CLI so that notmuch still compiles. Since this patch does not update the bindings, the Python bindings test fails. --- lib/database.cc | 28 +++++++++++++++++++++++----- lib/notmuch.h | 26 +++++++++++++++++++------- notmuch-count.c | 5 ++--- notmuch-dump.c | 5 ++--- notmuch-new.c | 5 ++--- notmuch-reply.c | 5 ++--- notmuch-restore.c | 5 ++--- notmuch-search.c | 5 ++--- notmuch-show.c | 5 ++--- notmuch-tag.c | 5 ++--- test/symbol-test.cc | 3 ++- 11 files changed, 60 insertions(+), 37 deletions(-) (limited to 'lib/notmuch.h') diff --git a/lib/database.cc b/lib/database.cc index 2fefcad..1e66599 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -556,8 +556,9 @@ notmuch_database_create (const char *path) goto DONE; } - notmuch = notmuch_database_open (path, - NOTMUCH_DATABASE_MODE_READ_WRITE); + notmuch_database_open (path, + NOTMUCH_DATABASE_MODE_READ_WRITE, + ¬much); notmuch_database_upgrade (notmuch, NULL, NULL); DONE: @@ -578,10 +579,12 @@ _notmuch_database_ensure_writable (notmuch_database_t *notmuch) return NOTMUCH_STATUS_SUCCESS; } -notmuch_database_t * +notmuch_status_t notmuch_database_open (const char *path, - notmuch_database_mode_t mode) + notmuch_database_mode_t mode, + notmuch_database_t **database) { + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; void *local = talloc_new (NULL); notmuch_database_t *notmuch = NULL; char *notmuch_path, *xapian_path; @@ -590,8 +593,15 @@ notmuch_database_open (const char *path, unsigned int i, version; static int initialized = 0; + if (path == NULL) { + fprintf (stderr, "Error: Cannot open a database for a NULL path.\n"); + status = NOTMUCH_STATUS_NULL_POINTER; + goto DONE; + } + if (! (notmuch_path = talloc_asprintf (local, "%s/%s", path, ".notmuch"))) { fprintf (stderr, "Out of memory\n"); + status = NOTMUCH_STATUS_OUT_OF_MEMORY; goto DONE; } @@ -599,11 +609,13 @@ notmuch_database_open (const char *path, if (err) { fprintf (stderr, "Error opening database at %s: %s\n", notmuch_path, strerror (errno)); + status = NOTMUCH_STATUS_FILE_ERROR; goto DONE; } if (! (xapian_path = talloc_asprintf (local, "%s/%s", notmuch_path, "xapian"))) { fprintf (stderr, "Out of memory\n"); + status = NOTMUCH_STATUS_OUT_OF_MEMORY; goto DONE; } @@ -644,6 +656,7 @@ notmuch_database_open (const char *path, notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY; notmuch_database_destroy (notmuch); notmuch = NULL; + status = NOTMUCH_STATUS_FILE_ERROR; goto DONE; } @@ -704,12 +717,17 @@ notmuch_database_open (const char *path, error.get_msg().c_str()); notmuch_database_destroy (notmuch); notmuch = NULL; + status = NOTMUCH_STATUS_XAPIAN_EXCEPTION; } DONE: talloc_free (local); - return notmuch; + if (database) + *database = notmuch; + else + talloc_free (notmuch); + return status; } void diff --git a/lib/notmuch.h b/lib/notmuch.h index 7d9e092..44b0c46 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -151,9 +151,6 @@ typedef enum { NOTMUCH_DATABASE_MODE_READ_WRITE } notmuch_database_mode_t; -/* XXX: I think I'd like this to take an extra argument of - * notmuch_status_t* for returning a status value on failure. */ - /* Open an existing notmuch database located at 'path'. * * The database should have been created at some time in the past, @@ -168,12 +165,27 @@ typedef enum { * The caller should call notmuch_database_destroy when finished with * this database. * - * In case of any failure, this function returns NULL, (after printing - * an error message on stderr). + * In case of any failure, this function returns an error status and + * sets *database to NULL (after printing an error message on stderr). + * + * Return value: + * + * NOTMUCH_STATUS_SUCCESS: Successfully opened the database. + * + * NOTMUCH_STATUS_NULL_POINTER: The given 'path' argument is NULL. + * + * NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory. + * + * NOTMUCH_STATUS_FILE_ERROR: An error occurred trying to open the + * database file (such as permission denied, or file not found, + * etc.), or the database version is unknown. + * + * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred. */ -notmuch_database_t * +notmuch_status_t notmuch_database_open (const char *path, - notmuch_database_mode_t mode); + notmuch_database_mode_t mode, + notmuch_database_t **database); /* Close the given notmuch database. * diff --git a/notmuch-count.c b/notmuch-count.c index 9c2ad7b..2f98128 100644 --- a/notmuch-count.c +++ b/notmuch-count.c @@ -66,9 +66,8 @@ notmuch_count_command (void *ctx, int argc, char *argv[]) if (config == NULL) return 1; - notmuch = notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY); - if (notmuch == NULL) + if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) return 1; query_str = query_string_from_args (ctx, argc-opt_index, argv+opt_index); diff --git a/notmuch-dump.c b/notmuch-dump.c index 71ab0ea..3743214 100644 --- a/notmuch-dump.c +++ b/notmuch-dump.c @@ -36,9 +36,8 @@ notmuch_dump_command (unused (void *ctx), int argc, char *argv[]) if (config == NULL) return 1; - notmuch = notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY); - if (notmuch == NULL) + if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) return 1; char *output_file_name = NULL; diff --git a/notmuch-new.c b/notmuch-new.c index 3ff6304..7788743 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -903,9 +903,8 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) notmuch = notmuch_database_create (db_path); add_files_state.total_files = count; } else { - notmuch = notmuch_database_open (db_path, - NOTMUCH_DATABASE_MODE_READ_WRITE); - if (notmuch == NULL) + if (notmuch_database_open (db_path, NOTMUCH_DATABASE_MODE_READ_WRITE, + ¬much)) return 1; if (notmuch_database_needs_upgrade (notmuch)) { diff --git a/notmuch-reply.c b/notmuch-reply.c index da99a13..7184a5d 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -740,9 +740,8 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) return 1; } - notmuch = notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY); - if (notmuch == NULL) + if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) return 1; query = notmuch_query_create (notmuch, query_string); diff --git a/notmuch-restore.c b/notmuch-restore.c index 02b563c..4f4096e 100644 --- a/notmuch-restore.c +++ b/notmuch-restore.c @@ -115,9 +115,8 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[]) if (config == NULL) return 1; - notmuch = notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_WRITE); - if (notmuch == NULL) + if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much)) return 1; synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); diff --git a/notmuch-search.c b/notmuch-search.c index 7dfd270..3be296d 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -486,9 +486,8 @@ notmuch_search_command (void *ctx, int argc, char *argv[]) if (config == NULL) return 1; - notmuch = notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY); - if (notmuch == NULL) + if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) return 1; query_str = query_string_from_args (notmuch, argc-opt_index, argv+opt_index); diff --git a/notmuch-show.c b/notmuch-show.c index 3b6667c..95427d4 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -1081,9 +1081,8 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) return 1; } - notmuch = notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY); - if (notmuch == NULL) + if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) return 1; query = notmuch_query_create (notmuch, query_string); diff --git a/notmuch-tag.c b/notmuch-tag.c index bd56fd1..7d18639 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -229,9 +229,8 @@ notmuch_tag_command (void *ctx, int argc, char *argv[]) if (config == NULL) return 1; - notmuch = notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_WRITE); - if (notmuch == NULL) + if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much)) return 1; synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); diff --git a/test/symbol-test.cc b/test/symbol-test.cc index 1548ca4..3e96c03 100644 --- a/test/symbol-test.cc +++ b/test/symbol-test.cc @@ -4,7 +4,8 @@ int main() { - (void) notmuch_database_open("fakedb", NOTMUCH_DATABASE_MODE_READ_ONLY); + notmuch_database_t *notmuch; + notmuch_database_open("fakedb", NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much); try { (void) new Xapian::WritableDatabase("./nonexistant", Xapian::DB_OPEN); -- cgit v1.2.3 From ba5729421825e0ec9d38aa9d656553f329aa6f09 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 30 Apr 2012 12:25:34 -0400 Subject: lib/cli: Make notmuch_database_create return a status code This is the notmuch_database_create equivalent of the previous change. In this case, there were places where errors were not being propagated correctly in notmuch_database_create or in calls to it. These have been fixed, using the new status value. --- lib/database.cc | 29 ++++++++++++++++++++++------- lib/notmuch.h | 22 ++++++++++++++++++---- notmuch-new.c | 3 ++- 3 files changed, 42 insertions(+), 12 deletions(-) (limited to 'lib/notmuch.h') diff --git a/lib/database.cc b/lib/database.cc index 1e66599..07f186e 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -520,9 +520,10 @@ parse_references (void *ctx, } } -notmuch_database_t * -notmuch_database_create (const char *path) +notmuch_status_t +notmuch_database_create (const char *path, notmuch_database_t **database) { + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; notmuch_database_t *notmuch = NULL; char *notmuch_path = NULL; struct stat st; @@ -530,6 +531,7 @@ notmuch_database_create (const char *path) if (path == NULL) { fprintf (stderr, "Error: Cannot create a database for a NULL path.\n"); + status = NOTMUCH_STATUS_NULL_POINTER; goto DONE; } @@ -537,12 +539,14 @@ notmuch_database_create (const char *path) if (err) { fprintf (stderr, "Error: Cannot create database at %s: %s.\n", path, strerror (errno)); + status = NOTMUCH_STATUS_FILE_ERROR; goto DONE; } if (! S_ISDIR (st.st_mode)) { fprintf (stderr, "Error: Cannot create database at %s: Not a directory.\n", path); + status = NOTMUCH_STATUS_FILE_ERROR; goto DONE; } @@ -553,19 +557,30 @@ notmuch_database_create (const char *path) if (err) { fprintf (stderr, "Error: Cannot create directory %s: %s.\n", notmuch_path, strerror (errno)); + status = NOTMUCH_STATUS_FILE_ERROR; goto DONE; } - notmuch_database_open (path, - NOTMUCH_DATABASE_MODE_READ_WRITE, - ¬much); - notmuch_database_upgrade (notmuch, NULL, NULL); + status = notmuch_database_open (path, + NOTMUCH_DATABASE_MODE_READ_WRITE, + ¬much); + if (status) + goto DONE; + status = notmuch_database_upgrade (notmuch, NULL, NULL); + if (status) { + notmuch_database_close(notmuch); + notmuch = NULL; + } DONE: if (notmuch_path) talloc_free (notmuch_path); - return notmuch; + if (database) + *database = notmuch; + else + talloc_free (notmuch); + return status; } notmuch_status_t diff --git a/lib/notmuch.h b/lib/notmuch.h index 44b0c46..e6e5cc2 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -140,11 +140,25 @@ typedef struct _notmuch_filenames notmuch_filenames_t; * contained within 'path' can be added to the database by calling * notmuch_database_add_message. * - * In case of any failure, this function returns NULL, (after printing - * an error message on stderr). + * In case of any failure, this function returns an error status and + * sets *database to NULL (after printing an error message on stderr). + * + * Return value: + * + * NOTMUCH_STATUS_SUCCESS: Successfully created the database. + * + * NOTMUCH_STATUS_NULL_POINTER: The given 'path' argument is NULL. + * + * NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory. + * + * NOTMUCH_STATUS_FILE_ERROR: An error occurred trying to create the + * database file (such as permission denied, or file not found, + * etc.), or the database already exists. + * + * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred. */ -notmuch_database_t * -notmuch_database_create (const char *path); +notmuch_status_t +notmuch_database_create (const char *path, notmuch_database_t **database); typedef enum { NOTMUCH_DATABASE_MODE_READ_ONLY = 0, diff --git a/notmuch-new.c b/notmuch-new.c index 7788743..cb720cc 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -900,7 +900,8 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) return 1; printf ("Found %d total files (that's not much mail).\n", count); - notmuch = notmuch_database_create (db_path); + if (notmuch_database_create (db_path, ¬much)) + return 1; add_files_state.total_files = count; } else { if (notmuch_database_open (db_path, NOTMUCH_DATABASE_MODE_READ_WRITE, -- cgit v1.2.3 From 7199d22f4394abdf72ab791fc0aba2c697bf1209 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Sun, 13 May 2012 19:36:09 -0400 Subject: lib/cli: Make notmuch_database_get_directory return a status code Previously, notmuch_database_get_directory had no way to indicate how it had failed. This changes its prototype to return a status code and set an out-argument to the retrieved directory, like similar functions in the library API. This does *not* change its currently broken behavior of creating directory objects when they don't exist, but it does document it and paves the way for fixing this. Also, it can now check for a read-only database and return NOTMUCH_STATUS_READ_ONLY_DATABASE instead of crashing. In the interest of atomicity, this also updates calls from the CLI so that notmuch still compiles. --- lib/database.cc | 22 ++++++++++++++++------ lib/notmuch.h | 23 ++++++++++++++++++++--- notmuch-new.c | 21 ++++++++++++++------- 3 files changed, 50 insertions(+), 16 deletions(-) (limited to 'lib/notmuch.h') diff --git a/lib/database.cc b/lib/database.cc index 07f186e..f8c4a7d 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -955,8 +955,8 @@ notmuch_database_upgrade (notmuch_database_t *notmuch, mtime = Xapian::sortable_unserialise ( document.get_value (NOTMUCH_VALUE_TIMESTAMP)); - directory = notmuch_database_get_directory (notmuch, - term.c_str() + 10); + directory = _notmuch_directory_create (notmuch, term.c_str() + 10, + &status); notmuch_directory_set_mtime (directory, mtime); notmuch_directory_destroy (directory); } @@ -1304,20 +1304,30 @@ _notmuch_database_relative_path (notmuch_database_t *notmuch, return relative; } -notmuch_directory_t * +notmuch_status_t notmuch_database_get_directory (notmuch_database_t *notmuch, - const char *path) + const char *path, + notmuch_directory_t **directory) { notmuch_status_t status; + if (directory == NULL) + return NOTMUCH_STATUS_NULL_POINTER; + *directory = NULL; + + /* XXX Handle read-only databases properly */ + if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY) + return NOTMUCH_STATUS_READ_ONLY_DATABASE; + try { - return _notmuch_directory_create (notmuch, path, &status); + *directory = _notmuch_directory_create (notmuch, path, &status); } catch (const Xapian::Error &error) { fprintf (stderr, "A Xapian exception occurred getting directory: %s.\n", error.get_msg().c_str()); notmuch->exception_reported = TRUE; - return NULL; + status = NOTMUCH_STATUS_XAPIAN_EXCEPTION; } + return status; } /* Allocate a document ID that satisfies the following criteria: diff --git a/lib/notmuch.h b/lib/notmuch.h index e6e5cc2..bbb17e4 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -300,11 +300,28 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch); * (see notmuch_database_get_path), or else should be an absolute path * with initial components that match the path of 'database'. * - * Can return NULL if a Xapian exception occurs. + * Note: Currently this will create the directory object if it doesn't + * exist. In the future, when a directory object does not exist this + * will return NOTMUCH_STATUS_SUCCESS and set *directory to NULL. + * Callers should be prepared for this. + * + * Return value: + * + * NOTMUCH_STATUS_SUCCESS: Successfully retrieved directory. + * + * NOTMUCH_STATUS_NULL_POINTER: The given 'directory' argument is NULL. + * + * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred; + * directory not retrieved. + * + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only + * mode so the directory cannot be created (this case will be + * removed in the future). */ -notmuch_directory_t * +notmuch_status_t notmuch_database_get_directory (notmuch_database_t *database, - const char *path); + const char *path, + notmuch_directory_t **directory); /* Add a new message to the given notmuch database or associate an * additional filename with an existing message. diff --git a/notmuch-new.c b/notmuch-new.c index cb720cc..a3a8bec 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -250,7 +250,7 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; notmuch_message_t *message = NULL; struct dirent **fs_entries = NULL; - int i, num_fs_entries; + int i, num_fs_entries = 0; notmuch_directory_t *directory; notmuch_filenames_t *db_files = NULL; notmuch_filenames_t *db_subdirs = NULL; @@ -274,8 +274,12 @@ add_files_recursive (notmuch_database_t *notmuch, fs_mtime = st.st_mtime; - directory = notmuch_database_get_directory (notmuch, path); - db_mtime = notmuch_directory_get_mtime (directory); + status = notmuch_database_get_directory (notmuch, path, &directory); + if (status) { + ret = status; + goto DONE; + } + db_mtime = directory ? notmuch_directory_get_mtime (directory) : 0; new_directory = db_mtime ? FALSE : TRUE; @@ -295,7 +299,7 @@ add_files_recursive (notmuch_database_t *notmuch, * by a new out-argument, or by recording this information and * providing an accessor. */ - if (new_directory) + if (new_directory && directory) notmuch_directory_set_mtime (directory, -1); /* If the database knows about this directory, then we sort based @@ -810,7 +814,9 @@ _remove_directory (void *ctx, notmuch_filenames_t *files, *subdirs; char *absolute; - directory = notmuch_database_get_directory (notmuch, path); + status = notmuch_database_get_directory (notmuch, path, &directory); + if (status || !directory) + return status; for (files = notmuch_directory_get_child_files (directory); notmuch_filenames_valid (files); @@ -981,9 +987,10 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) } for (f = add_files_state.directory_mtimes->head; f && !interrupted; f = f->next) { + notmuch_status_t status; notmuch_directory_t *directory; - directory = notmuch_database_get_directory (notmuch, f->filename); - if (directory) { + status = notmuch_database_get_directory (notmuch, f->filename, &directory); + if (status == NOTMUCH_STATUS_SUCCESS && directory) { notmuch_directory_set_mtime (directory, f->mtime); notmuch_directory_destroy (directory); } -- cgit v1.2.3 From fe1ca1410423d99db09543f4a97bc2ba0c6ade81 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 18 May 2012 00:13:37 -0400 Subject: lib: Make notmuch_database_get_directory return NULL if the directory is not found Using the new support from _notmuch_directory_create, this makes notmuch_database_get_directory a read-only operation that simply returns the directory object if it exists or NULL otherwise. This also means that notmuch_database_get_directory can work on read-only databases. This change breaks the directory mtime workaround in notmuch-new.c by fixing the exact issue it was working around. This permits mtime update races to prevent scans of changed directories, which non-deterministically breaks a few tests. The next patch fixes this. --- lib/database.cc | 7 ++----- lib/notmuch-private.h | 3 +++ lib/notmuch.h | 10 ++-------- 3 files changed, 7 insertions(+), 13 deletions(-) (limited to 'lib/notmuch.h') diff --git a/lib/database.cc b/lib/database.cc index b4c76b4..e27a0e1 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -1328,12 +1328,9 @@ notmuch_database_get_directory (notmuch_database_t *notmuch, return NOTMUCH_STATUS_NULL_POINTER; *directory = NULL; - /* XXX Handle read-only databases properly */ - if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY) - return NOTMUCH_STATUS_READ_ONLY_DATABASE; - try { - *directory = _notmuch_directory_create (notmuch, path, NOTMUCH_FIND_CREATE, &status); + *directory = _notmuch_directory_create (notmuch, path, + NOTMUCH_FIND_LOOKUP, &status); } catch (const Xapian::Error &error) { fprintf (stderr, "A Xapian exception occurred getting directory: %s.\n", error.get_msg().c_str()); diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 34f7ac7..bfb4111 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -148,6 +148,9 @@ typedef enum _notmuch_private_status { /* Flags shared by various lookup functions. */ typedef enum _notmuch_find_flags { + /* Lookup without creating any documents. This is the default + * behavior. */ + NOTMUCH_FIND_LOOKUP = 0, /* If set, create the necessary document (or documents) if they * are missing. Requires a read/write database. */ NOTMUCH_FIND_CREATE = 1<<0, diff --git a/lib/notmuch.h b/lib/notmuch.h index bbb17e4..3633bed 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -300,10 +300,8 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch); * (see notmuch_database_get_path), or else should be an absolute path * with initial components that match the path of 'database'. * - * Note: Currently this will create the directory object if it doesn't - * exist. In the future, when a directory object does not exist this - * will return NOTMUCH_STATUS_SUCCESS and set *directory to NULL. - * Callers should be prepared for this. + * If this directory object does not exist in the database, this + * returns NOTMUCH_STATUS_SUCCESS and sets *directory to NULL. * * Return value: * @@ -313,10 +311,6 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch); * * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred; * directory not retrieved. - * - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only - * mode so the directory cannot be created (this case will be - * removed in the future). */ notmuch_status_t notmuch_database_get_directory (notmuch_database_t *database, -- cgit v1.2.3