From 4595a814c2117fb33ea39e3ecea41858ad3791a2 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 24 Aug 2021 08:17:44 -0700 Subject: [PATCH] CLI/tag: enable sexp queries We have to rewrite _optimize_tag_query here because it is generating a query string in the infix Xapian syntax. Luckily this is easy to do with the sexp query syntax. --- notmuch-tag.c | 43 ++++++++++++++++++++++++++++++++++++------- test/T150-tagging.sh | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/notmuch-tag.c b/notmuch-tag.c index aa886032..71ff06bf 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -39,8 +39,8 @@ handle_sigint (unused (int sig)) static char * -_optimize_tag_query (void *ctx, const char *orig_query_string, - const tag_op_list_t *list) +_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, @@ -88,6 +88,33 @@ _optimize_tag_query (void *ctx, const char *orig_query_string, 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) +{ + char *query_string; + + if (stx == NOTMUCH_QUERY_SYNTAX_XAPIAN) + return _optimize_tag_query_infix (ctx, orig_query_string, list); + + /* Don't optimize if there are no tag changes. */ + if (tag_op_list_size (list) == 0) + return talloc_strdup (ctx, orig_query_string); + + query_string = talloc_asprintf (ctx, "(and %s", orig_query_string); + for (size_t i = 0; i < tag_op_list_size (list) && query_string; i++) { + query_string = talloc_asprintf_append_buffer ( + query_string, tag_op_list_isremove (list, i) ? " (tag \"%s\")" : " (not (tag \"%s\"))", + tag_op_list_tag (list, i)); + } + + if (query_string) + query_string = talloc_strdup_append_buffer (query_string, ")"); + + return query_string; +} + /* Tag messages matching 'query_string' according to 'tag_ops' */ static int @@ -104,7 +131,9 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string, if (! (flags & TAG_FLAG_REMOVE_ALL)) { /* Optimize the query so it excludes messages that already * have the specified set of tags. */ - query_string = _optimize_tag_query (ctx, query_string, tag_ops); + query_string = _optimize_tag_query (ctx, query_string, + shared_option_query_syntax (), + tag_ops); if (query_string == NULL) { fprintf (stderr, "Out of memory.\n"); return 1; @@ -112,11 +141,11 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string, flags |= TAG_FLAG_PRE_OPTIMIZED; } - query = notmuch_query_create (notmuch, query_string); - if (query == NULL) { - fprintf (stderr, "Out of memory.\n"); + status = notmuch_query_create_with_syntax (notmuch, query_string, + shared_option_query_syntax (), + &query); + if (print_status_database ("notmuch tag", notmuch, status)) return 1; - } /* tagging is not interested in any special sort order */ notmuch_query_set_sort (query, NOTMUCH_SORT_UNSORTED); diff --git a/test/T150-tagging.sh b/test/T150-tagging.sh index c292b24e..1a2fd77e 100755 --- a/test/T150-tagging.sh +++ b/test/T150-tagging.sh @@ -2,6 +2,21 @@ test_description='"notmuch tag"' . $(dirname "$0")/test-lib.sh || exit 1 +test_query_syntax () { + # use a tag with a space to stress the query string munging code. + local new_tag="${RANDOM} ${RANDOM}" + test_begin_subtest "sexpr query: $1" + backup_database + notmuch tag --query=sexp "+${new_tag}" -- "$1" + notmuch dump > OUTPUT + restore_database + backup_database + notmuch tag "+${new_tag}" -- "$2" + notmuch dump > EXPECTED + restore_database + test_expect_equal_file_nonempty EXPECTED OUTPUT +} + add_message '[subject]=One' add_message '[subject]=Two' @@ -310,4 +325,32 @@ output=$(notmuch tag +something '*' 2>&1 | sed 's/: .*$//' ) chmod u+w ${MAIL_DIR}/.notmuch/xapian/*.* test_expect_equal "$output" "A Xapian exception occurred opening database" +add_email_corpus + +if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then + + test_query_syntax '(and "wonderful" "wizard")' 'wonderful and wizard' + test_query_syntax '(or "php" "wizard")' 'php or wizard' + test_query_syntax 'wizard' 'wizard' + test_query_syntax 'Wizard' 'Wizard' + test_query_syntax '(attachment notmuch-help.patch)' 'attachment:notmuch-help.patch' + test_query_syntax '(mimetype text/html)' 'mimetype:text/html' + + test_begin_subtest "--batch --query=sexp" + notmuch dump --format=batch-tag > backup.tags + notmuch tag --batch --query=sexp < OUTPUT + cat < EXPECTED + #notmuch-dump batch-tag:3 config,properties,tags + +all +inbox +tag5 +unread -- id:msg-001@notmuch-test-suite + +all +inbox +tag4 +tag5 +unread -- id:msg-002@notmuch-test-suite +EOF + notmuch restore --format=batch-tag < backup.tags + test_expect_equal_file EXPECTED OUTPUT + +fi + test_done