+
+static char *
+_optimize_tag_query_infix (void *ctx, const char *orig_query_string,
+ const tag_op_list_t *list)
+{
+ /* This is subtler than it looks. Xapian ignores the '-' operator
+ * at the beginning both queries and parenthesized groups and,
+ * furthermore, the presence of a '-' operator at the beginning of
+ * a group can inhibit parsing of the previous operator. Hence,
+ * the user-provided query MUST appear first, but it is safe to
+ * parenthesize and the exclusion part of the query must not use
+ * the '-' operator (though the NOT operator is fine). */
+
+ char *escaped = NULL;
+ size_t escaped_len = 0;
+ char *query_string;
+ const char *join = "";
+ size_t i;
+
+ /* Don't optimize if there are no tag changes. */
+ if (tag_op_list_size (list) == 0)
+ return talloc_strdup (ctx, orig_query_string);
+
+ /* Build the new query string */
+ if (strcmp (orig_query_string, "*") == 0)
+ query_string = talloc_strdup (ctx, "(");
+ else
+ query_string = talloc_asprintf (ctx, "( %s ) and (", orig_query_string);
+
+ for (i = 0; i < tag_op_list_size (list) && query_string; i++) {
+ /* XXX in case of OOM, query_string will be deallocated when
+ * ctx is, which might be at shutdown */
+ if (make_boolean_term (ctx,
+ "tag", tag_op_list_tag (list, i),
+ &escaped, &escaped_len))
+ return NULL;
+
+ query_string = talloc_asprintf_append_buffer (
+ query_string, "%s%s%s", join,
+ tag_op_list_isremove (list, i) ? "" : "not ",
+ escaped);
+ join = " or ";
+ }
+
+ if (query_string)
+ query_string = talloc_strdup_append_buffer (query_string, ")");
+
+ talloc_free (escaped);
+ return query_string;
+}
+
+static char *
+_optimize_tag_query (void *ctx, const char *orig_query_string,
+ notmuch_query_syntax_t stx,
+ const tag_op_list_t *list)