mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-11-21 18:38:08 +01:00
lib/parse-sexp: support infix subqueries
This is necessary so that programs can take infix syntax queries from a user and use the sexp query syntax to construct e.g. a refinement of that query.
This commit is contained in:
parent
afe85e6578
commit
cc5992a304
3 changed files with 84 additions and 0 deletions
|
@ -144,6 +144,10 @@ MODIFIERS
|
||||||
*Modifiers* refer to any prefixes (first elements of compound queries)
|
*Modifiers* refer to any prefixes (first elements of compound queries)
|
||||||
that are neither operators nor fields.
|
that are neither operators nor fields.
|
||||||
|
|
||||||
|
``(infix`` *atom* ``)``
|
||||||
|
Interpret *atom* as an infix notmuch query (see
|
||||||
|
:any:`notmuch-search-terms(7)`). Not supported inside fields.
|
||||||
|
|
||||||
``(matching`` |q1| |q2| ... |qn| ``)`` ``(of`` |q1| |q2| ... |qn| ``)``
|
``(matching`` |q1| |q2| ... |qn| ``)`` ``(of`` |q1| |q2| ... |qn| ``)``
|
||||||
Match all messages have the same values of the current field as
|
Match all messages have the same values of the current field as
|
||||||
those matching all of |q1| ... |qn|. Supported in most term [#not-path]_ or
|
those matching all of |q1| ... |qn|. Supported in most term [#not-path]_ or
|
||||||
|
@ -187,6 +191,9 @@ EXAMPLES
|
||||||
``(id 1234@invalid blah@test)``
|
``(id 1234@invalid blah@test)``
|
||||||
Matches Message-Id "1234@invalid" *or* Message-Id "blah@test"
|
Matches Message-Id "1234@invalid" *or* Message-Id "blah@test"
|
||||||
|
|
||||||
|
``(and (infix "date:2009-11-18..2009-11-18") (tag unread))``
|
||||||
|
Match messages in the given date range with tag unread.
|
||||||
|
|
||||||
``(starts-with prelim)``
|
``(starts-with prelim)``
|
||||||
Match any words starting with "prelim".
|
Match any words starting with "prelim".
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,8 @@ static _sexp_prefix_t prefixes[] =
|
||||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
||||||
{ "id", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
|
{ "id", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
|
||||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX },
|
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX },
|
||||||
|
{ "infix", Xapian::Query::OP_INVALID, Xapian::Query::MatchAll,
|
||||||
|
SEXP_FLAG_SINGLE },
|
||||||
{ "is", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
{ "is", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
||||||
{ "matching", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
{ "matching", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||||
|
@ -242,6 +244,34 @@ _sexp_expand_query (notmuch_database_t *notmuch,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static notmuch_status_t
|
||||||
|
_sexp_parse_infix (notmuch_database_t *notmuch, const _sexp_prefix_t *parent,
|
||||||
|
const sexp_t *sx, Xapian::Query &output)
|
||||||
|
{
|
||||||
|
if (parent) {
|
||||||
|
_notmuch_database_log (notmuch, "'infix' not supported inside '%s'\n", parent->name);
|
||||||
|
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
output = notmuch->query_parser->parse_query (sx->val, NOTMUCH_QUERY_PARSER_FLAGS);
|
||||||
|
} catch (const Xapian::QueryParserError &error) {
|
||||||
|
_notmuch_database_log (notmuch, "Syntax error in infix query: %s\n", sx->val);
|
||||||
|
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||||
|
} catch (const Xapian::Error &error) {
|
||||||
|
if (! notmuch->exception_reported) {
|
||||||
|
_notmuch_database_log (notmuch,
|
||||||
|
"A Xapian exception occurred parsing query: %s\n",
|
||||||
|
error.get_msg ().c_str ());
|
||||||
|
_notmuch_database_log_append (notmuch,
|
||||||
|
"Query string was: %s\n",
|
||||||
|
sx->val);
|
||||||
|
notmuch->exception_reported = true;
|
||||||
|
return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NOTMUCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/* Here we expect the s-expression to be a proper list, with first
|
/* Here we expect the s-expression to be a proper list, with first
|
||||||
* element defining and operation, or as a special case the empty
|
* element defining and operation, or as a special case the empty
|
||||||
* list */
|
* list */
|
||||||
|
@ -311,6 +341,10 @@ _sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent
|
||||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmp (prefix->name, "infix") == 0) {
|
||||||
|
return _sexp_parse_infix (notmuch, parent, sx->list->next, output);
|
||||||
|
}
|
||||||
|
|
||||||
if (prefix->xapian_op == Xapian::Query::OP_WILDCARD)
|
if (prefix->xapian_op == Xapian::Query::OP_WILDCARD)
|
||||||
return _sexp_parse_wildcard (notmuch, parent, sx->list->next->val, output);
|
return _sexp_parse_wildcard (notmuch, parent, sx->list->next->val, output);
|
||||||
|
|
||||||
|
|
|
@ -689,4 +689,47 @@ id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com
|
||||||
EOF
|
EOF
|
||||||
test_expect_equal_file EXPECTED OUTPUT
|
test_expect_equal_file EXPECTED OUTPUT
|
||||||
|
|
||||||
|
test_begin_subtest "infix query"
|
||||||
|
notmuch search to:searchbyto | notmuch_search_sanitize > EXPECTED
|
||||||
|
notmuch search --query=sexp '(infix "to:searchbyto")' | notmuch_search_sanitize > OUTPUT
|
||||||
|
test_expect_equal_file EXPECTED OUTPUT
|
||||||
|
|
||||||
|
test_begin_subtest "bad infix query 1"
|
||||||
|
notmuch search --query=sexp '(infix "from:/unbalanced")' 2>&1| notmuch_search_sanitize > OUTPUT
|
||||||
|
cat <<EOF > EXPECTED
|
||||||
|
notmuch search: Syntax error in query
|
||||||
|
Syntax error in infix query: from:/unbalanced
|
||||||
|
EOF
|
||||||
|
test_expect_equal_file EXPECTED OUTPUT
|
||||||
|
|
||||||
|
test_begin_subtest "bad infix query 2"
|
||||||
|
notmuch search --query=sexp '(infix "thread:{unbalanced")' 2>&1| notmuch_search_sanitize > OUTPUT
|
||||||
|
cat <<EOF > EXPECTED
|
||||||
|
notmuch search: Syntax error in query
|
||||||
|
Syntax error in infix query: thread:{unbalanced
|
||||||
|
EOF
|
||||||
|
test_expect_equal_file EXPECTED OUTPUT
|
||||||
|
|
||||||
|
test_begin_subtest "bad infix query 3: bad nesting"
|
||||||
|
notmuch search --query=sexp '(subject (infix "tag:inbox"))' 2>&1| notmuch_search_sanitize > OUTPUT
|
||||||
|
cat <<EOF > EXPECTED
|
||||||
|
notmuch search: Syntax error in query
|
||||||
|
'infix' not supported inside 'subject'
|
||||||
|
EOF
|
||||||
|
test_expect_equal_file EXPECTED OUTPUT
|
||||||
|
|
||||||
|
test_begin_subtest "infix query that matches no messages"
|
||||||
|
notmuch search --query=sexp '(and (infix "from:keithp") (infix "to:keithp"))' > OUTPUT
|
||||||
|
test_expect_equal_file /dev/null OUTPUT
|
||||||
|
|
||||||
|
test_begin_subtest "compound infix query"
|
||||||
|
notmuch search date:2009-11-18..2009-11-18 and tag:unread > EXPECTED
|
||||||
|
notmuch search --query=sexp '(infix "date:2009-11-18..2009-11-18 and tag:unread")' > OUTPUT
|
||||||
|
test_expect_equal_file EXPECTED OUTPUT
|
||||||
|
|
||||||
|
test_begin_subtest "compound infix query 2"
|
||||||
|
notmuch search date:2009-11-18..2009-11-18 and tag:unread > EXPECTED
|
||||||
|
notmuch search --query=sexp '(and (infix "date:2009-11-18..2009-11-18") (infix "tag:unread"))' > OUTPUT
|
||||||
|
test_expect_equal_file EXPECTED OUTPUT
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
Loading…
Reference in a new issue