1 #include "database-private.h"
5 #include "unicode-util.h"
7 /* _sexp is used for file scope symbols to avoid clashing with
8 * definitions from sexp.h */
12 SEXP_FLAG_FIELD = 1 << 0,
13 SEXP_FLAG_BOOLEAN = 1 << 1,
17 * define bitwise operators to hide casts */
20 operator| (_sexp_flag_t a, _sexp_flag_t b)
22 return static_cast<_sexp_flag_t>(
23 static_cast<unsigned>(a) | static_cast<unsigned>(b));
27 operator& (_sexp_flag_t a, _sexp_flag_t b)
29 return static_cast<_sexp_flag_t>(
30 static_cast<unsigned>(a) & static_cast<unsigned>(b));
35 Xapian::Query::op xapian_op;
36 Xapian::Query initial;
40 static _sexp_prefix_t prefixes[] =
42 { "and", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
44 { "attachment", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
46 { "body", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
48 { "from", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
50 { "folder", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
51 SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN },
52 { "id", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
53 SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN },
54 { "is", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
55 SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN },
56 { "mid", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
57 SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN },
58 { "mimetype", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
60 { "not", Xapian::Query::OP_AND_NOT, Xapian::Query::MatchAll,
62 { "or", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
64 { "path", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
65 SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN },
66 { "property", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
68 | SEXP_FLAG_BOOLEAN },
69 { "subject", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
71 { "tag", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
72 SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN },
73 { "thread", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
74 SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN },
75 { "to", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
80 static notmuch_status_t _sexp_to_xapian_query (notmuch_database_t *notmuch,
81 const _sexp_prefix_t *parent,
83 Xapian::Query &output);
85 static notmuch_status_t
86 _sexp_combine_query (notmuch_database_t *notmuch,
87 const _sexp_prefix_t *parent,
88 Xapian::Query::op operation,
91 Xapian::Query &output)
93 Xapian::Query subquery;
95 notmuch_status_t status;
97 /* if we run out elements, return accumulator */
101 return NOTMUCH_STATUS_SUCCESS;
104 status = _sexp_to_xapian_query (notmuch, parent, sx, subquery);
108 return _sexp_combine_query (notmuch,
111 Xapian::Query (operation, left, subquery),
115 static notmuch_status_t
116 _sexp_parse_phrase (std::string term_prefix, const char *phrase, Xapian::Query &output)
118 Xapian::Utf8Iterator p (phrase);
119 Xapian::Utf8Iterator end;
120 std::vector<std::string> terms;
123 Xapian::Utf8Iterator start;
124 while (p != end && ! Xapian::Unicode::is_wordchar (*p))
132 while (p != end && Xapian::Unicode::is_wordchar (*p))
136 std::string word (start, p);
137 word = Xapian::Unicode::tolower (word);
138 terms.push_back (term_prefix + word);
141 output = Xapian::Query (Xapian::Query::OP_PHRASE, terms.begin (), terms.end ());
142 return NOTMUCH_STATUS_SUCCESS;
145 /* Here we expect the s-expression to be a proper list, with first
146 * element defining and operation, or as a special case the empty
149 static notmuch_status_t
150 _sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent, const sexp_t *sx,
151 Xapian::Query &output)
154 if (sx->ty == SEXP_VALUE) {
155 std::string term = Xapian::Unicode::tolower (sx->val);
156 Xapian::Stem stem = *(notmuch->stemmer);
157 std::string term_prefix = parent ? _find_prefix (parent->name) : "";
158 if (parent && (parent->flags & SEXP_FLAG_BOOLEAN)) {
159 output = Xapian::Query (term_prefix + sx->val);
160 return NOTMUCH_STATUS_SUCCESS;
162 if (sx->aty == SEXP_BASIC && unicode_word_utf8 (sx->val)) {
163 output = Xapian::Query ("Z" + term_prefix + stem (term));
164 return NOTMUCH_STATUS_SUCCESS;
166 return _sexp_parse_phrase (term_prefix, sx->val, output);
172 output = Xapian::Query::MatchAll;
173 return NOTMUCH_STATUS_SUCCESS;
176 if (sx->list->ty == SEXP_LIST) {
177 _notmuch_database_log (notmuch, "unexpected list in field/operation position\n",
179 return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
182 for (_sexp_prefix_t *prefix = prefixes; prefix && prefix->name; prefix++) {
183 if (strcmp (prefix->name, sx->list->val) == 0) {
184 if (prefix->flags & SEXP_FLAG_FIELD) {
186 _notmuch_database_log (notmuch, "nested field: '%s' inside '%s'\n",
187 prefix->name, parent->name);
188 return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
193 return _sexp_combine_query (notmuch, parent, prefix->xapian_op, prefix->initial,
194 sx->list->next, output);
198 _notmuch_database_log (notmuch, "unknown prefix '%s'\n", sx->list->val);
200 return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
204 _notmuch_sexp_string_to_xapian_query (notmuch_database_t *notmuch, const char *querystr,
205 Xapian::Query &output)
207 const sexp_t *sx = NULL;
208 char *buf = talloc_strdup (notmuch, querystr);
210 sx = parse_sexp (buf, strlen (querystr));
212 _notmuch_database_log (notmuch, "invalid s-expression: '%s'\n", querystr);
213 return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
216 return _sexp_to_xapian_query (notmuch, NULL, sx, output);