+ unsigned int doc_id;
+ notmuch_private_status_t status;
+ notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
+ char *db_key = NULL;
+
+ db_key = timestamp_db_key (key);
+
+ try {
+ status = find_timestamp_document (notmuch, db_key, &doc, &doc_id);
+
+ doc.add_value (NOTMUCH_VALUE_TIMESTAMP,
+ Xapian::sortable_serialise (timestamp));
+
+ if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
+ char *term = talloc_asprintf (NULL, "%s%s",
+ _find_prefix ("timestamp"), db_key);
+ doc.add_term (term);
+ talloc_free (term);
+
+ notmuch->xapian_db->add_document (doc);
+ } else {
+ notmuch->xapian_db->replace_document (doc_id, doc);
+ }
+
+ } catch (Xapian::Error &error) {
+ fprintf (stderr, "A Xapian exception occurred: %s.\n",
+ error.get_msg().c_str());
+ ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ }
+
+ if (db_key)
+ free (db_key);
+
+ return ret;
+}
+
+time_t
+notmuch_database_get_timestamp (notmuch_database_t *notmuch, const char *key)
+{
+ Xapian::Document doc;
+ unsigned int doc_id;
+ notmuch_private_status_t status;
+ char *db_key = NULL;
+ time_t ret = 0;
+
+ db_key = timestamp_db_key (key);
+
+ try {
+ status = find_timestamp_document (notmuch, db_key, &doc, &doc_id);
+
+ if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND)
+ goto DONE;
+
+ ret = Xapian::sortable_unserialise (doc.get_value (NOTMUCH_VALUE_TIMESTAMP));
+ } catch (Xapian::Error &error) {
+ goto DONE;
+ }
+
+ DONE:
+ if (db_key)
+ free (db_key);
+
+ return ret;
+}
+
+/* Find the thread ID to which the message with 'message_id' belongs.
+ *
+ * Returns NULL if no message with message ID 'message_id' is in the
+ * database.
+ *
+ * Otherwise, returns a newly talloced string belonging to 'ctx'.
+ */
+static const char *
+_resolve_message_id_to_thread_id (notmuch_database_t *notmuch,
+ void *ctx,
+ const char *message_id)
+{
+ notmuch_message_t *message;
+ const char *ret = NULL;
+
+ message = notmuch_database_find_message (notmuch, message_id);
+ if (message == NULL)
+ goto DONE;
+
+ ret = talloc_steal (ctx, notmuch_message_get_thread_id (message));
+
+ DONE:
+ if (message)
+ notmuch_message_destroy (message);
+
+ return ret;
+}
+
+static notmuch_status_t
+_merge_threads (notmuch_database_t *notmuch,
+ const char *winner_thread_id,
+ const char *loser_thread_id)
+{
+ Xapian::PostingIterator loser, loser_end;
+ notmuch_message_t *message = NULL;
+ notmuch_private_status_t private_status;
+ notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
+
+ find_doc_ids (notmuch, "thread", loser_thread_id, &loser, &loser_end);
+
+ for ( ; loser != loser_end; loser++) {
+ message = _notmuch_message_create (notmuch, notmuch,
+ *loser, &private_status);
+ if (message == NULL) {
+ ret = COERCE_STATUS (private_status,
+ "Cannot find document for doc_id from query");
+ goto DONE;
+ }
+
+ _notmuch_message_remove_term (message, "thread", loser_thread_id);
+ _notmuch_message_add_term (message, "thread", winner_thread_id);
+ _notmuch_message_sync (message);
+
+ notmuch_message_destroy (message);
+ message = NULL;
+ }
+
+ DONE:
+ if (message)
+ notmuch_message_destroy (message);
+
+ return ret;
+}
+
+static void
+_my_talloc_free_for_g_hash (void *ptr)
+{
+ talloc_free (ptr);
+}
+
+static notmuch_status_t
+_notmuch_database_link_message_to_parents (notmuch_database_t *notmuch,
+ notmuch_message_t *message,
+ notmuch_message_file_t *message_file,
+ const char **thread_id)
+{
+ GHashTable *parents = NULL;
+ const char *refs, *in_reply_to;
+ GList *l, *keys = NULL;