return "Syntax error in query";
case NOTMUCH_STATUS_NO_MAIL_ROOT:
return "No mail root found";
+ case NOTMUCH_STATUS_ITERATOR_EXHAUSTED:
+ return "Iterator exhausted";
+ case NOTMUCH_STATUS_OPERATION_INVALIDATED:
+ return "Operation invalidated due to concurrent database modification";
default:
case NOTMUCH_STATUS_LAST_STATUS:
return "Unknown error status value";
if (status)
*status = NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND;
return NULL;
+ } catch (const Xapian::DatabaseModifiedError &error) {
+ if (status)
+ *status = NOTMUCH_PRIVATE_STATUS_OPERATION_INVALIDATED;
+ return NULL;
}
return _notmuch_message_create_for_document (talloc_owner, notmuch,
messages->is_of_list_type = true;
messages->iterator = list->head;
+ messages->status = NOTMUCH_STATUS_SUCCESS;
return messages;
}
* carefully control object construction with placement new
* anyway. *sigh*
*/
-notmuch_bool_t
-notmuch_messages_valid (notmuch_messages_t *messages)
+notmuch_status_t
+notmuch_messages_status (notmuch_messages_t *messages)
{
+ bool valid;
+
if (messages == NULL)
- return false;
+ return NOTMUCH_STATUS_ITERATOR_EXHAUSTED;
+
+ if (messages->status != NOTMUCH_STATUS_SUCCESS)
+ return messages->status;
if (! messages->is_of_list_type)
- return _notmuch_mset_messages_valid (messages);
+ valid = _notmuch_mset_messages_valid (messages);
+ else
+ valid = messages->iterator != NULL;
+
+ return valid ?
+ NOTMUCH_STATUS_SUCCESS : NOTMUCH_STATUS_ITERATOR_EXHAUSTED;
+}
- return (messages->iterator != NULL);
+notmuch_bool_t
+notmuch_messages_valid (notmuch_messages_t *messages)
+{
+ return notmuch_messages_status (messages) == NOTMUCH_STATUS_SUCCESS;
}
bool
return;
}
- if (messages->iterator == NULL)
+ if (! notmuch_messages_valid (messages))
return;
messages->iterator = messages->iterator->next;
NOTMUCH_PRIVATE_STATUS_NO_MAIL_ROOT = NOTMUCH_STATUS_NO_MAIL_ROOT,
NOTMUCH_PRIVATE_STATUS_BAD_QUERY_SYNTAX = NOTMUCH_STATUS_BAD_QUERY_SYNTAX,
NOTMUCH_PRIVATE_STATUS_CLOSED_DATABASE = NOTMUCH_STATUS_CLOSED_DATABASE,
+ NOTMUCH_PRIVATE_STATUS_ITERATOR_EXHAUSTED = NOTMUCH_STATUS_ITERATOR_EXHAUSTED,
+ NOTMUCH_PRIVATE_STATUS_OPERATION_INVALIDATED = NOTMUCH_STATUS_OPERATION_INVALIDATED,
/* Then add our own private values. */
NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG = NOTMUCH_STATUS_LAST_STATUS,
bool is_of_list_type;
notmuch_doc_id_set_t *excluded_doc_ids;
notmuch_message_node_t *iterator;
+ notmuch_status_t status;
};
notmuch_message_list_t *
* Database is not fully opened, or has been closed
*/
NOTMUCH_STATUS_CLOSED_DATABASE,
+ /**
+ * The iterator being examined has been exhausted and contains no more
+ * items.
+ */
+ NOTMUCH_STATUS_ITERATOR_EXHAUSTED,
+ /**
+ * An operation that was being performed on the database has been
+ * invalidated while in progress, and must be re-executed.
+ *
+ * This will typically happen while iterating over query results and the
+ * underlying Xapian database is modified by another process so that the
+ * currently open version cannot be read anymore.
+ */
+ NOTMUCH_STATUS_OPERATION_INVALIDATED,
/**
* Not an actual status value. Just a way to find out how many
* valid status values there are.
* return EXIT_FAILURE;
*
* for (;
- * notmuch_messages_valid (messages);
+ * ! notmuch_messages_status (messages);
* notmuch_messages_move_to_next (messages))
* {
* message = notmuch_messages_get (messages);
* notmuch_message_destroy (message);
* }
*
+ * if (notmuch_messages_status (messages) != NOTMUCH_STATUS_ITERATOR_EXHAUSTED)
+ * return EXIT_FAILURE;
+ *
* notmuch_query_destroy (query);
*
* Note: If you are finished with a message before its containing
*
* See the documentation of notmuch_query_search_messages for example
* code showing how to iterate over a notmuch_messages_t object.
+ *
+ * Note that an iterator may become invalid either due to getting exhausted or
+ * due to a runtime error. Use notmuch_messages_status to distinguish
+ * between those cases.
*/
notmuch_bool_t
notmuch_messages_valid (notmuch_messages_t *messages);
+/**
+ * Get the status of the given 'messages' iterator.
+ *
+ * Return value:
+ *
+ * NOTMUCH_STATUS_SUCCESS: The iterator is valid; notmuch_messages_get will
+ * return a valid object
+ *
+ * NOTMUCH_STATUS_ITERATOR_EXHAUSTED: All items have been read
+ *
+ * NOTMUCH_STATUS_OUT_OF_MEMORY: Iteration failed to allocate memory
+ *
+ * NOTMUCH_STATUS_OPERATION_INVALIDATED: Iteration was invalidated by the
+ * database. Re-open the database and try again.
+ *
+ * See the documentation of notmuch_query_search_messages for example
+ * code showing how to iterate over a notmuch_messages_t object.
+ */
+notmuch_status_t
+notmuch_messages_status (notmuch_messages_t *messages);
+
/**
* Get the current message from 'messages' as a notmuch_message_t.
*
*
* If 'messages' is already pointing at the last message then the
* iterator will be moved to a point just beyond that last message,
- * (where notmuch_messages_valid will return FALSE and
- * notmuch_messages_get will return NULL).
+ * (where notmuch_messages_status will return NOTMUCH_STATUS_ITERATOR_EXHAUSTED
+ * and notmuch_messages_get will return NULL).
*
* See the documentation of notmuch_query_search_messages for example
* code showing how to iterate over a notmuch_messages_t object.
* will return NULL.
*
* If there are no replies to 'message', this function will return
- * NULL. (Note that notmuch_messages_valid will accept that NULL
- * value as legitimate, and simply return FALSE for it.)
+ * NULL. (Note that notmuch_messages_status will accept that NULL
+ * value as legitimate, and simply return NOTMUCH_STATUS_ITERATOR_EXHAUSTED
+ * for it.)
*
* This function also returns NULL if it triggers a Xapian exception.
*
messages->base.is_of_list_type = false;
messages->base.iterator = NULL;
+ messages->base.status = NOTMUCH_STATUS_SUCCESS;
messages->notmuch = notmuch;
new (&messages->iterator) Xapian::MSetIterator ();
new (&messages->iterator_end) Xapian::MSetIterator ();
mset_messages->notmuch, doc_id,
&status);
- if (message == NULL &&
- status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
- INTERNAL_ERROR ("a messages iterator contains a non-existent document ID.\n");
+ if (message == NULL) {
+ if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
+ INTERNAL_ERROR ("a messages iterator contains a non-existent document ID.\n");
+ } else if (status != NOTMUCH_PRIVATE_STATUS_SUCCESS) {
+ messages->status = COERCE_STATUS (status, "error creating a message");
+ return NULL;
+ }
+
+ INTERNAL_ERROR ("NULL message with no error code\n");
}
if (messages->excluded_doc_ids &&
cp -a $NOTMUCH_SRCDIR/test/corpora/lkml ${MAIL_DIR}/
test_begin_subtest "catching DatabaseModifiedError in _notmuch_message_create"
-test_subtest_known_broken
test_C ${MAIL_DIR} <<EOF
#include <notmuch-test.h>
notmuch_database_t *rw_db, *ro_db;
notmuch_messages_t *messages_ro, *messages_rw;
notmuch_query_t *query_ro, *query_rw;
+ notmuch_status_t status;
char* msg = NULL;
+ unsigned try;
EXPECT0 (notmuch_database_open_with_config (argv[1],
NOTMUCH_DATABASE_MODE_READ_ONLY,
EXPECT0 (notmuch_query_search_messages (query_rw, &messages_rw));
for (;
- notmuch_messages_valid (messages_rw);
+ ! notmuch_messages_status (messages_rw);
notmuch_messages_move_to_next (messages_rw)) {
notmuch_message_t *message = notmuch_messages_get (messages_rw);
EXPECT0 (notmuch_message_add_tag (message, "tag"));
notmuch_database_close (rw_db);
- for (;
- notmuch_messages_valid (messages_ro);
- notmuch_messages_move_to_next (messages_ro)) {
- notmuch_message_t *message = notmuch_messages_get (messages_ro);
+ // try iterating over the query up to twice, we expect a Xapian
+ // DatabaseModifiedError (mapped to NOTMUCH_STATUS_OPERATION_INVALIDATED)
+ // on the first try
+ for (try = 0; try < 2; try++) {
+ for (;
+ ! notmuch_messages_status (messages_ro);
+ notmuch_messages_move_to_next (messages_ro)) {
+ notmuch_message_t *message = notmuch_messages_get (messages_ro);
+ }
+ status = notmuch_messages_status (messages_ro);
+ if (status != NOTMUCH_STATUS_OPERATION_INVALIDATED)
+ break;
+
+ notmuch_query_destroy (query_ro);
+ notmuch_database_close (ro_db);
+
+ EXPECT0 (notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_ONLY,
+ "", NULL, &ro_db, &msg));
+ query_ro = notmuch_query_create (ro_db, "");
+ assert (query_ro);
+ EXPECT0 (notmuch_query_search_messages (query_ro, &messages_ro));
}
- printf ("SUCCESS\n");
+ if (status == NOTMUCH_STATUS_ITERATOR_EXHAUSTED)
+ printf ("SUCCESS\n");
+ else
+ printf ("status: %s\n", notmuch_status_to_string (status));
return 0;
}
EOF