X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=lib%2Fquery.cc;h=77a7926ba80d6f13d1ca62ff7827bd18c9ef9c45;hp=c25b301db4d7e1fc3b122ed7f6bfa95bc1c73e43;hb=011fc41d4d1a1dc9ae3765c9f08ee603eea7bc7e;hpb=08f7b026a9f8a32fbe14eb73b99a026544b22900 diff --git a/lib/query.cc b/lib/query.cc index c25b301d..77a7926b 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_exclude_t omit_excluded; }; typedef struct _notmuch_mset_messages { @@ -38,12 +39,12 @@ typedef struct _notmuch_mset_messages { } notmuch_mset_messages_t; struct _notmuch_doc_id_set { - unsigned int *bitmap; + unsigned char *bitmap; unsigned int bound; }; -#define DOCIDSET_WORD(bit) ((bit) / sizeof (unsigned int)) -#define DOCIDSET_BIT(bit) ((bit) % sizeof (unsigned int)) +#define DOCIDSET_WORD(bit) ((bit) / CHAR_BIT) +#define DOCIDSET_BIT(bit) ((bit) % CHAR_BIT) struct visible _notmuch_threads { notmuch_query_t *query; @@ -57,17 +58,29 @@ 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); + +static notmuch_bool_t +_debug_query (void) +{ + char *env = getenv ("NOTMUCH_DEBUG_QUERY"); + return (env && strcmp (env, "") != 0); +} + notmuch_query_t * notmuch_query_create (notmuch_database_t *notmuch, const char *query_string) { notmuch_query_t *query; -#ifdef DEBUG_QUERY - fprintf (stderr, "Query string is:\n%s\n", query_string); -#endif + if (_debug_query ()) + fprintf (stderr, "Query string is:\n%s\n", query_string); - query = talloc (NULL, notmuch_query_t); + query = talloc (notmuch, notmuch_query_t); if (unlikely (query == NULL)) return NULL; @@ -79,15 +92,24 @@ notmuch_query_create (notmuch_database_t *notmuch, query->exclude_terms = _notmuch_string_list_create (query); + query->omit_excluded = NOTMUCH_EXCLUDE_TRUE; + return query; } 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; } +void +notmuch_query_set_omit_excluded (notmuch_query_t *query, + notmuch_exclude_t omit_excluded) +{ + query->omit_excluded = omit_excluded; +} + void notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort) { @@ -95,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; } @@ -124,8 +146,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,12 +165,35 @@ _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; } notmuch_messages_t * notmuch_query_search_messages (notmuch_query_t *query) +{ + notmuch_status_t status; + notmuch_messages_t *messages; + status = notmuch_query_search_messages_st (query, &messages); + if (status) + return NULL; + else + return messages; +} + +notmuch_status_t +notmuch_query_search_messages_st (notmuch_query_t *query, + notmuch_messages_t **out) +{ + return _notmuch_query_search_documents (query, "mail", out); +} + +notmuch_status_t +_notmuch_query_search_documents (notmuch_query_t *query, + const char *type, + notmuch_messages_t **out) { notmuch_database_t *notmuch = query->notmuch; const char *query_string = query->query_string; @@ -155,7 +201,7 @@ notmuch_query_search_messages (notmuch_query_t *query) messages = talloc (query, notmuch_mset_messages_t); if (unlikely (messages == NULL)) - return NULL; + return NOTMUCH_STATUS_OUT_OF_MEMORY; try { @@ -170,9 +216,10 @@ notmuch_query_search_messages (notmuch_query_t *query) Xapian::Enquire enquire (*notmuch->xapian_db); Xapian::Query mail_query (talloc_asprintf (query, "%s%s", _find_prefix ("type"), - "mail")); + type)); 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 +237,38 @@ 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->omit_excluded != NOTMUCH_EXCLUDE_FALSE) && (query->exclude_terms)) { + exclude_query = _notmuch_exclude_tags (query, final_query); + + if (query->omit_excluded == NOTMUCH_EXCLUDE_TRUE || + query->omit_excluded == NOTMUCH_EXCLUDE_ALL) + { + final_query = Xapian::Query (Xapian::Query::OP_AND_NOT, + final_query, exclude_query); + } else { /* NOTMUCH_EXCLUDE_FLAG */ + exclude_query = Xapian::Query (Xapian::Query::OP_AND, + exclude_query, final_query); + + 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()); @@ -212,9 +286,12 @@ notmuch_query_search_messages (notmuch_query_t *query) break; } -#if DEBUG_QUERY - fprintf (stderr, "Final query is:\n%s\n", final_query.get_description().c_str()); -#endif + if (_debug_query ()) { + fprintf (stderr, "Exclude query is:\n%s\n", + exclude_query.get_description ().c_str ()); + fprintf (stderr, "Final query is:\n%s\n", + final_query.get_description ().c_str ()); + } enquire.set_query (final_query); @@ -223,15 +300,19 @@ notmuch_query_search_messages (notmuch_query_t *query) messages->iterator = mset.begin (); messages->iterator_end = mset.end (); - return &messages->base; + *out = &messages->base; + return NOTMUCH_STATUS_SUCCESS; } catch (const Xapian::Error &error) { - fprintf (stderr, "A Xapian exception occurred performing query: %s\n", - error.get_msg().c_str()); - fprintf (stderr, "Query string was: %s\n", query->query_string); + _notmuch_database_log (notmuch, + "A Xapian exception occurred performing query: %s\n" + "Query string was: %s\n", + error.get_msg().c_str(), + query->query_string); + notmuch->exception_reported = TRUE; talloc_free (messages); - return NULL; + return NOTMUCH_STATUS_XAPIAN_EXCEPTION; } } @@ -283,6 +364,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; } @@ -302,11 +387,11 @@ _notmuch_doc_id_set_init (void *ctx, GArray *arr) { unsigned int max = 0; - unsigned int *bitmap; + unsigned char *bitmap; for (unsigned int i = 0; i < arr->len; i++) max = MAX(max, g_array_index (arr, unsigned int, i)); - bitmap = talloc_zero_array (ctx, unsigned int, 1 + max / sizeof (*bitmap)); + bitmap = talloc_zero_array (ctx, unsigned char, DOCIDSET_WORD(max) + 1); if (bitmap == NULL) return FALSE; @@ -352,24 +437,39 @@ _notmuch_threads_destructor (notmuch_threads_t *threads) return 0; } + notmuch_threads_t * notmuch_query_search_threads (notmuch_query_t *query) +{ + notmuch_status_t status; + notmuch_threads_t *threads; + status = notmuch_query_search_threads_st (query, &threads); + if (status) + return NULL; + else + return threads; +} + +notmuch_status_t +notmuch_query_search_threads_st (notmuch_query_t *query, + notmuch_threads_t **out) { notmuch_threads_t *threads; notmuch_messages_t *messages; + notmuch_status_t status; threads = talloc (query, notmuch_threads_t); if (threads == NULL) - return NULL; + return NOTMUCH_STATUS_OUT_OF_MEMORY; threads->doc_ids = NULL; talloc_set_destructor (threads, _notmuch_threads_destructor); threads->query = query; - messages = notmuch_query_search_messages (query); - if (messages == NULL) { - talloc_free (threads); - return NULL; + status = notmuch_query_search_messages_st (query, &messages); + if (status) { + talloc_free (threads); + return status; } threads->doc_ids = g_array_new (FALSE, FALSE, sizeof (unsigned int)); @@ -385,10 +485,11 @@ notmuch_query_search_threads (notmuch_query_t *query) if (! _notmuch_doc_id_set_init (threads, &threads->match_set, threads->doc_ids)) { talloc_free (threads); - return NULL; + return NOTMUCH_STATUS_OUT_OF_MEMORY; } - return threads; + *out = threads; + return NOTMUCH_STATUS_SUCCESS; } void @@ -402,6 +503,9 @@ notmuch_threads_valid (notmuch_threads_t *threads) { unsigned int doc_id; + if (! threads) + return FALSE; + while (threads->doc_id_pos < threads->doc_ids->len) { doc_id = g_array_index (threads->doc_ids, unsigned int, threads->doc_id_pos); @@ -428,6 +532,8 @@ notmuch_threads_get (notmuch_threads_t *threads) threads->query->notmuch, doc_id, &threads->match_set, + threads->query->exclude_terms, + threads->query->omit_excluded, threads->query->sort); } @@ -443,8 +549,24 @@ 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) +{ + return _notmuch_query_count_documents (query, "mail", count_out); +} + +notmuch_status_t +_notmuch_query_count_documents (notmuch_query_t *query, const char *type, unsigned *count_out) { notmuch_database_t *notmuch = query->notmuch; const char *query_string = query->query_string; @@ -454,7 +576,7 @@ notmuch_query_count_messages (notmuch_query_t *query) Xapian::Enquire enquire (*notmuch->xapian_db); Xapian::Query mail_query (talloc_asprintf (query, "%s%s", _find_prefix ("type"), - "mail")); + type)); Xapian::Query string_query, final_query, exclude_query; Xapian::MSet mset; unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN | @@ -483,44 +605,68 @@ notmuch_query_count_messages (notmuch_query_t *query) enquire.set_weighting_scheme(Xapian::BoolWeight()); enquire.set_docid_order(Xapian::Enquire::ASCENDING); -#if DEBUG_QUERY - fprintf (stderr, "Final query is:\n%s\n", final_query.get_description().c_str()); -#endif + if (_debug_query ()) { + fprintf (stderr, "Exclude query is:\n%s\n", + exclude_query.get_description ().c_str ()); + fprintf (stderr, "Final query is:\n%s\n", + final_query.get_description ().c_str ()); + } enquire.set_query (final_query); - mset = enquire.get_mset (0, notmuch->xapian_db->get_doccount ()); + /* + * Set the checkatleast parameter to the number of documents + * in the database to make get_matches_estimated() exact. + */ + mset = enquire.get_mset (0, notmuch->xapian_db->get_doccount (), + notmuch->xapian_db->get_doccount ()); count = mset.get_matches_estimated(); } catch (const Xapian::Error &error) { - fprintf (stderr, "A Xapian exception occurred: %s\n", - error.get_msg().c_str()); - fprintf (stderr, "Query string was: %s\n", query->query_string); + _notmuch_database_log (notmuch, + "A Xapian exception occurred performing query: %s\n" + "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)) { @@ -529,7 +675,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); @@ -537,11 +683,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; }