]>
Commit | Line | Data |
---|---|---|
11fdf7f2 | 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
9f95a23c | 2 | // vim: ts=8 sw=2 smarttab ft=cpp |
11fdf7f2 | 3 | |
31f18b77 FG |
4 | #include <list> |
5 | #include <map> | |
6 | #include <string> | |
7 | #include <iostream> | |
8 | #include <boost/algorithm/string.hpp> | |
9 | ||
10 | #include "common/ceph_json.h" | |
11 | #include "rgw_common.h" | |
12 | #include "rgw_es_query.h" | |
13 | ||
31f18b77 FG |
14 | |
15 | #define dout_context g_ceph_context | |
16 | #define dout_subsys ceph_subsys_rgw | |
17 | ||
20effc67 TL |
18 | using namespace std; |
19 | ||
31f18b77 FG |
20 | bool pop_front(list<string>& l, string *s) |
21 | { | |
22 | if (l.empty()) { | |
23 | return false; | |
24 | } | |
25 | *s = l.front(); | |
26 | l.pop_front(); | |
27 | return true; | |
28 | } | |
29 | ||
30 | map<string, int> operator_map = { | |
31 | { "or", 1 }, | |
32 | { "and", 2 }, | |
33 | { "<", 3 }, | |
34 | { "<=", 3 }, | |
35 | { "==", 3 }, | |
a8e16298 | 36 | { "!=", 3 }, |
31f18b77 FG |
37 | { ">=", 3 }, |
38 | { ">", 3 }, | |
39 | }; | |
40 | ||
41 | bool is_operator(const string& s) | |
42 | { | |
43 | return (operator_map.find(s) != operator_map.end()); | |
44 | } | |
45 | ||
46 | int operand_value(const string& op) | |
47 | { | |
48 | auto i = operator_map.find(op); | |
49 | if (i == operator_map.end()) { | |
50 | return 0; | |
51 | } | |
52 | ||
53 | return i->second; | |
54 | } | |
55 | ||
56 | int check_precedence(const string& op1, const string& op2) | |
57 | { | |
58 | return operand_value(op1) - operand_value(op2); | |
59 | } | |
60 | ||
61 | static bool infix_to_prefix(list<string>& source, list<string> *out) | |
62 | { | |
63 | list<string> operator_stack; | |
64 | list<string> operand_stack; | |
65 | ||
66 | operator_stack.push_front("("); | |
67 | source.push_back(")"); | |
68 | ||
69 | for (string& entity : source) { | |
70 | if (entity == "(") { | |
71 | operator_stack.push_front(entity); | |
72 | } else if (entity == ")") { | |
73 | string popped_operator; | |
74 | if (!pop_front(operator_stack, &popped_operator)) { | |
75 | return false; | |
76 | } | |
77 | ||
78 | while (popped_operator != "(") { | |
79 | operand_stack.push_front(popped_operator); | |
80 | if (!pop_front(operator_stack, &popped_operator)) { | |
81 | return false; | |
82 | } | |
83 | } | |
84 | ||
85 | } else if (is_operator(entity)) { | |
86 | string popped_operator; | |
87 | if (!pop_front(operator_stack, &popped_operator)) { | |
88 | return false; | |
89 | } | |
90 | ||
91 | int precedence = check_precedence(popped_operator, entity); | |
92 | ||
93 | while (precedence >= 0) { | |
94 | operand_stack.push_front(popped_operator); | |
95 | if (!pop_front(operator_stack, &popped_operator)) { | |
96 | return false; | |
97 | } | |
98 | precedence = check_precedence(popped_operator, entity); | |
99 | } | |
100 | ||
101 | operator_stack.push_front(popped_operator); | |
102 | operator_stack.push_front(entity); | |
103 | } else { | |
104 | operand_stack.push_front(entity); | |
105 | } | |
106 | ||
107 | } | |
108 | ||
109 | if (!operator_stack.empty()) { | |
110 | return false; | |
111 | } | |
112 | ||
113 | out->swap(operand_stack); | |
114 | return true; | |
115 | } | |
116 | ||
117 | class ESQueryNode { | |
118 | protected: | |
119 | ESQueryCompiler *compiler; | |
120 | public: | |
121 | ESQueryNode(ESQueryCompiler *_compiler) : compiler(_compiler) {} | |
122 | virtual ~ESQueryNode() {} | |
123 | ||
124 | virtual bool init(ESQueryStack *s, ESQueryNode **pnode, string *perr) = 0; | |
125 | ||
126 | virtual void dump(Formatter *f) const = 0; | |
127 | }; | |
128 | ||
129 | static bool alloc_node(ESQueryCompiler *compiler, ESQueryStack *s, ESQueryNode **pnode, string *perr); | |
130 | ||
131 | class ESQueryNode_Bool : public ESQueryNode { | |
132 | string op; | |
133 | ESQueryNode *first{nullptr}; | |
134 | ESQueryNode *second{nullptr}; | |
135 | public: | |
11fdf7f2 | 136 | explicit ESQueryNode_Bool(ESQueryCompiler *compiler) : ESQueryNode(compiler) {} |
31f18b77 FG |
137 | ESQueryNode_Bool(ESQueryCompiler *compiler, const string& _op, ESQueryNode *_first, ESQueryNode *_second) :ESQueryNode(compiler), op(_op), first(_first), second(_second) {} |
138 | bool init(ESQueryStack *s, ESQueryNode **pnode, string *perr) override { | |
139 | bool valid = s->pop(&op); | |
140 | if (!valid) { | |
141 | *perr = "incorrect expression"; | |
142 | return false; | |
143 | } | |
144 | valid = alloc_node(compiler, s, &first, perr) && | |
145 | alloc_node(compiler, s, &second, perr); | |
146 | if (!valid) { | |
147 | return false; | |
148 | } | |
149 | *pnode = this; | |
150 | return true; | |
151 | } | |
152 | virtual ~ESQueryNode_Bool() { | |
153 | delete first; | |
154 | delete second; | |
155 | } | |
156 | ||
11fdf7f2 | 157 | void dump(Formatter *f) const override { |
31f18b77 FG |
158 | f->open_object_section("bool"); |
159 | const char *section = (op == "and" ? "must" : "should"); | |
160 | f->open_array_section(section); | |
161 | encode_json("entry", *first, f); | |
162 | encode_json("entry", *second, f); | |
163 | f->close_section(); | |
164 | f->close_section(); | |
165 | } | |
166 | ||
167 | }; | |
168 | ||
169 | class ESQueryNodeLeafVal { | |
170 | public: | |
171 | ESQueryNodeLeafVal() = default; | |
172 | virtual ~ESQueryNodeLeafVal() {} | |
173 | ||
174 | virtual bool init(const string& str_val, string *perr) = 0; | |
175 | virtual void encode_json(const string& field, Formatter *f) const = 0; | |
176 | }; | |
177 | ||
178 | class ESQueryNodeLeafVal_Str : public ESQueryNodeLeafVal { | |
179 | string val; | |
180 | public: | |
181 | ESQueryNodeLeafVal_Str() {} | |
182 | bool init(const string& str_val, string *perr) override { | |
183 | val = str_val; | |
184 | return true; | |
185 | } | |
11fdf7f2 | 186 | void encode_json(const string& field, Formatter *f) const override { |
31f18b77 FG |
187 | ::encode_json(field.c_str(), val.c_str(), f); |
188 | } | |
189 | }; | |
190 | ||
191 | class ESQueryNodeLeafVal_Int : public ESQueryNodeLeafVal { | |
224ce89b | 192 | int64_t val{0}; |
31f18b77 FG |
193 | public: |
194 | ESQueryNodeLeafVal_Int() {} | |
195 | bool init(const string& str_val, string *perr) override { | |
196 | string err; | |
197 | val = strict_strtoll(str_val.c_str(), 10, &err); | |
198 | if (!err.empty()) { | |
199 | *perr = string("failed to parse integer: ") + err; | |
200 | return false; | |
201 | } | |
202 | return true; | |
203 | } | |
11fdf7f2 | 204 | void encode_json(const string& field, Formatter *f) const override { |
31f18b77 FG |
205 | ::encode_json(field.c_str(), val, f); |
206 | } | |
207 | }; | |
208 | ||
209 | class ESQueryNodeLeafVal_Date : public ESQueryNodeLeafVal { | |
210 | ceph::real_time val; | |
211 | public: | |
212 | ESQueryNodeLeafVal_Date() {} | |
213 | bool init(const string& str_val, string *perr) override { | |
214 | if (parse_time(str_val.c_str(), &val) < 0) { | |
215 | *perr = string("failed to parse date: ") + str_val; | |
216 | return false; | |
217 | } | |
218 | return true; | |
219 | } | |
11fdf7f2 | 220 | void encode_json(const string& field, Formatter *f) const override { |
31f18b77 FG |
221 | string s; |
222 | rgw_to_iso8601(val, &s); | |
223 | ::encode_json(field.c_str(), s, f); | |
224 | } | |
225 | }; | |
226 | ||
227 | class ESQueryNode_Op : public ESQueryNode { | |
228 | protected: | |
229 | string op; | |
230 | string field; | |
231 | string str_val; | |
232 | ESQueryNodeLeafVal *val{nullptr}; | |
233 | ESEntityTypeMap::EntityType entity_type{ESEntityTypeMap::ES_ENTITY_NONE}; | |
234 | bool allow_restricted{false}; | |
235 | ||
236 | bool val_from_str(string *perr) { | |
237 | switch (entity_type) { | |
238 | case ESEntityTypeMap::ES_ENTITY_DATE: | |
239 | val = new ESQueryNodeLeafVal_Date; | |
240 | break; | |
241 | case ESEntityTypeMap::ES_ENTITY_INT: | |
242 | val = new ESQueryNodeLeafVal_Int; | |
243 | break; | |
244 | default: | |
245 | val = new ESQueryNodeLeafVal_Str; | |
246 | } | |
247 | return val->init(str_val, perr); | |
248 | } | |
249 | bool do_init(ESQueryNode **pnode, string *perr) { | |
250 | field = compiler->unalias_field(field); | |
251 | ESQueryNode *effective_node; | |
252 | if (!handle_nested(&effective_node, perr)) { | |
253 | return false; | |
254 | } | |
255 | if (!val_from_str(perr)) { | |
256 | return false; | |
257 | } | |
258 | *pnode = effective_node; | |
259 | return true; | |
260 | } | |
261 | ||
262 | public: | |
263 | ESQueryNode_Op(ESQueryCompiler *compiler) : ESQueryNode(compiler) {} | |
264 | ~ESQueryNode_Op() { | |
265 | delete val; | |
266 | } | |
267 | virtual bool init(ESQueryStack *s, ESQueryNode **pnode, string *perr) override { | |
268 | bool valid = s->pop(&op) && | |
269 | s->pop(&str_val) && | |
270 | s->pop(&field); | |
271 | if (!valid) { | |
272 | *perr = "invalid expression"; | |
273 | return false; | |
274 | } | |
275 | return do_init(pnode, perr); | |
276 | } | |
277 | bool handle_nested(ESQueryNode **pnode, string *perr); | |
278 | ||
279 | void set_allow_restricted(bool allow) { | |
280 | allow_restricted = allow; | |
281 | } | |
282 | ||
11fdf7f2 | 283 | virtual void dump(Formatter *f) const override = 0; |
31f18b77 FG |
284 | }; |
285 | ||
286 | class ESQueryNode_Op_Equal : public ESQueryNode_Op { | |
287 | public: | |
11fdf7f2 | 288 | explicit ESQueryNode_Op_Equal(ESQueryCompiler *compiler) : ESQueryNode_Op(compiler) {} |
31f18b77 FG |
289 | ESQueryNode_Op_Equal(ESQueryCompiler *compiler, const string& f, const string& v) : ESQueryNode_Op(compiler) { |
290 | op = "=="; | |
291 | field = f; | |
292 | str_val = v; | |
293 | } | |
294 | ||
295 | bool init(ESQueryStack *s, ESQueryNode **pnode, string *perr) override { | |
296 | if (op.empty()) { | |
297 | return ESQueryNode_Op::init(s, pnode, perr); | |
298 | } | |
299 | return do_init(pnode, perr); | |
300 | } | |
301 | ||
11fdf7f2 | 302 | virtual void dump(Formatter *f) const override { |
31f18b77 FG |
303 | f->open_object_section("term"); |
304 | val->encode_json(field, f); | |
305 | f->close_section(); | |
306 | } | |
307 | }; | |
308 | ||
a8e16298 TL |
309 | class ESQueryNode_Op_NotEqual : public ESQueryNode_Op { |
310 | public: | |
311 | explicit ESQueryNode_Op_NotEqual(ESQueryCompiler *compiler) : ESQueryNode_Op(compiler) {} | |
312 | ESQueryNode_Op_NotEqual(ESQueryCompiler *compiler, const string& f, const string& v) : ESQueryNode_Op(compiler) { | |
313 | op = "!="; | |
314 | field = f; | |
315 | str_val = v; | |
316 | } | |
317 | ||
318 | bool init(ESQueryStack *s, ESQueryNode **pnode, string *perr) override { | |
319 | if (op.empty()) { | |
320 | return ESQueryNode_Op::init(s, pnode, perr); | |
321 | } | |
322 | return do_init(pnode, perr); | |
323 | } | |
324 | ||
325 | virtual void dump(Formatter *f) const override { | |
326 | f->open_object_section("bool"); | |
327 | f->open_object_section("must_not"); | |
328 | f->open_object_section("term"); | |
329 | val->encode_json(field, f); | |
330 | f->close_section(); | |
331 | f->close_section(); | |
332 | f->close_section(); | |
333 | } | |
334 | }; | |
335 | ||
31f18b77 FG |
336 | class ESQueryNode_Op_Range : public ESQueryNode_Op { |
337 | string range_str; | |
338 | public: | |
339 | ESQueryNode_Op_Range(ESQueryCompiler *compiler, const string& rs) : ESQueryNode_Op(compiler), range_str(rs) {} | |
340 | ||
11fdf7f2 | 341 | virtual void dump(Formatter *f) const override { |
31f18b77 FG |
342 | f->open_object_section("range"); |
343 | f->open_object_section(field.c_str()); | |
344 | val->encode_json(range_str, f); | |
345 | f->close_section(); | |
346 | f->close_section(); | |
347 | } | |
348 | }; | |
349 | ||
350 | class ESQueryNode_Op_Nested_Parent : public ESQueryNode_Op { | |
351 | public: | |
352 | ESQueryNode_Op_Nested_Parent(ESQueryCompiler *compiler) : ESQueryNode_Op(compiler) {} | |
353 | ||
354 | virtual string get_custom_leaf_field_name() = 0; | |
355 | }; | |
356 | ||
357 | template <class T> | |
358 | class ESQueryNode_Op_Nested : public ESQueryNode_Op_Nested_Parent { | |
359 | string name; | |
360 | ESQueryNode *next; | |
361 | public: | |
362 | ESQueryNode_Op_Nested(ESQueryCompiler *compiler, const string& _name, ESQueryNode *_next) : ESQueryNode_Op_Nested_Parent(compiler), | |
363 | name(_name), next(_next) {} | |
364 | ~ESQueryNode_Op_Nested() { | |
365 | delete next; | |
366 | } | |
367 | ||
11fdf7f2 | 368 | virtual void dump(Formatter *f) const override { |
31f18b77 FG |
369 | f->open_object_section("nested"); |
370 | string s = string("meta.custom-") + type_str(); | |
371 | encode_json("path", s.c_str(), f); | |
372 | f->open_object_section("query"); | |
373 | f->open_object_section("bool"); | |
374 | f->open_array_section("must"); | |
375 | f->open_object_section("entry"); | |
376 | f->open_object_section("match"); | |
377 | string n = s + ".name"; | |
378 | encode_json(n.c_str(), name.c_str(), f); | |
379 | f->close_section(); | |
380 | f->close_section(); | |
381 | encode_json("entry", *next, f); | |
382 | f->close_section(); | |
383 | f->close_section(); | |
384 | f->close_section(); | |
385 | f->close_section(); | |
386 | } | |
387 | ||
388 | string type_str() const; | |
11fdf7f2 | 389 | string get_custom_leaf_field_name() override { |
31f18b77 FG |
390 | return string("meta.custom-") + type_str() + ".value"; |
391 | } | |
392 | }; | |
393 | ||
394 | template<> | |
395 | string ESQueryNode_Op_Nested<string>::type_str() const { | |
396 | return "string"; | |
397 | } | |
398 | ||
399 | template<> | |
400 | string ESQueryNode_Op_Nested<int64_t>::type_str() const { | |
401 | return "int"; | |
402 | } | |
403 | ||
404 | template<> | |
405 | string ESQueryNode_Op_Nested<ceph::real_time>::type_str() const { | |
406 | return "date"; | |
407 | } | |
408 | ||
409 | bool ESQueryNode_Op::handle_nested(ESQueryNode **pnode, string *perr) | |
410 | { | |
411 | string field_name = field; | |
412 | const string& custom_prefix = compiler->get_custom_prefix(); | |
413 | if (!boost::algorithm::starts_with(field_name, custom_prefix)) { | |
414 | *pnode = this; | |
415 | auto m = compiler->get_generic_type_map(); | |
416 | if (m) { | |
417 | bool found = m->find(field_name, &entity_type) && | |
418 | (allow_restricted || !compiler->is_restricted(field_name)); | |
419 | if (!found) { | |
420 | *perr = string("unexpected generic field '") + field_name + "'"; | |
421 | } | |
422 | return found; | |
423 | } | |
424 | *perr = "query parser does not support generic types"; | |
425 | return false; | |
426 | } | |
427 | ||
428 | field_name = field_name.substr(custom_prefix.size()); | |
429 | auto m = compiler->get_custom_type_map(); | |
430 | if (m) { | |
431 | m->find(field_name, &entity_type); | |
432 | /* ignoring returned bool, for now just treat it as string */ | |
433 | } | |
434 | ||
435 | ESQueryNode_Op_Nested_Parent *new_node; | |
436 | switch (entity_type) { | |
437 | case ESEntityTypeMap::ES_ENTITY_INT: | |
438 | new_node = new ESQueryNode_Op_Nested<int64_t>(compiler, field_name, this); | |
439 | break; | |
440 | case ESEntityTypeMap::ES_ENTITY_DATE: | |
441 | new_node = new ESQueryNode_Op_Nested<ceph::real_time>(compiler, field_name, this); | |
442 | break; | |
443 | default: | |
444 | new_node = new ESQueryNode_Op_Nested<string>(compiler, field_name, this); | |
445 | } | |
446 | ||
447 | field = new_node->get_custom_leaf_field_name(); | |
448 | *pnode = new_node; | |
449 | ||
450 | return true; | |
451 | } | |
452 | ||
453 | static bool is_bool_op(const string& str) | |
454 | { | |
455 | return (str == "or" || str == "and"); | |
456 | } | |
457 | ||
458 | static bool alloc_node(ESQueryCompiler *compiler, ESQueryStack *s, ESQueryNode **pnode, string *perr) | |
459 | { | |
460 | string op; | |
461 | bool valid = s->peek(&op); | |
462 | if (!valid) { | |
463 | *perr = "incorrect expression"; | |
464 | return false; | |
465 | } | |
466 | ||
467 | ESQueryNode *node; | |
468 | ||
469 | if (is_bool_op(op)) { | |
470 | node = new ESQueryNode_Bool(compiler); | |
471 | } else if (op == "==") { | |
472 | node = new ESQueryNode_Op_Equal(compiler); | |
a8e16298 TL |
473 | } else if (op == "!=") { |
474 | node = new ESQueryNode_Op_NotEqual(compiler); | |
31f18b77 FG |
475 | } else { |
476 | static map<string, string> range_op_map = { | |
477 | { "<", "lt"}, | |
478 | { "<=", "lte"}, | |
479 | { ">=", "gte"}, | |
480 | { ">", "gt"}, | |
481 | }; | |
482 | ||
483 | auto iter = range_op_map.find(op); | |
484 | if (iter == range_op_map.end()) { | |
485 | *perr = string("invalid operator: ") + op; | |
486 | return false; | |
487 | } | |
488 | ||
489 | node = new ESQueryNode_Op_Range(compiler, iter->second); | |
490 | } | |
491 | ||
492 | if (!node->init(s, pnode, perr)) { | |
493 | delete node; | |
494 | return false; | |
495 | } | |
496 | return true; | |
497 | } | |
498 | ||
499 | ||
500 | bool is_key_char(char c) | |
501 | { | |
502 | switch (c) { | |
503 | case '(': | |
504 | case ')': | |
505 | case '<': | |
506 | case '>': | |
a8e16298 | 507 | case '!': |
31f18b77 FG |
508 | case '@': |
509 | case ',': | |
510 | case ';': | |
511 | case ':': | |
512 | case '\\': | |
513 | case '"': | |
514 | case '/': | |
515 | case '[': | |
516 | case ']': | |
517 | case '?': | |
518 | case '=': | |
519 | case '{': | |
520 | case '}': | |
521 | case ' ': | |
522 | case '\t': | |
523 | return false; | |
524 | }; | |
525 | return (isascii(c) > 0); | |
526 | } | |
527 | ||
528 | static bool is_op_char(char c) | |
529 | { | |
530 | switch (c) { | |
a8e16298 | 531 | case '!': |
31f18b77 FG |
532 | case '<': |
533 | case '=': | |
534 | case '>': | |
535 | return true; | |
536 | }; | |
537 | return false; | |
538 | } | |
539 | ||
540 | static bool is_val_char(char c) | |
541 | { | |
542 | if (isspace(c)) { | |
543 | return false; | |
544 | } | |
545 | return (c != ')'); | |
546 | } | |
547 | ||
548 | void ESInfixQueryParser::skip_whitespace(const char *str, int size, int& pos) { | |
549 | while (pos < size && isspace(str[pos])) { | |
550 | ++pos; | |
551 | } | |
552 | } | |
553 | ||
554 | bool ESInfixQueryParser::get_next_token(bool (*filter)(char)) { | |
555 | skip_whitespace(str, size, pos); | |
556 | int token_start = pos; | |
557 | while (pos < size && filter(str[pos])) { | |
558 | ++pos; | |
559 | } | |
560 | if (pos == token_start) { | |
561 | return false; | |
562 | } | |
563 | string token = string(str + token_start, pos - token_start); | |
564 | args.push_back(token); | |
565 | return true; | |
566 | } | |
567 | ||
568 | bool ESInfixQueryParser::parse_condition() { | |
569 | /* | |
570 | * condition: <key> <operator> <val> | |
571 | * | |
572 | * whereas key: needs to conform to http header field restrictions | |
a8e16298 | 573 | * operator: one of the following: < <= == != >= > |
31f18b77 FG |
574 | * val: ascii, terminated by either space or ')' (or end of string) |
575 | */ | |
576 | ||
577 | /* parse key */ | |
578 | bool valid = get_next_token(is_key_char) && | |
579 | get_next_token(is_op_char) && | |
580 | get_next_token(is_val_char); | |
581 | ||
582 | if (!valid) { | |
583 | return false; | |
584 | } | |
585 | ||
586 | return true; | |
587 | } | |
588 | ||
589 | bool ESInfixQueryParser::parse_and_or() { | |
590 | skip_whitespace(str, size, pos); | |
591 | if (pos + 3 <= size && strncmp(str + pos, "and", 3) == 0) { | |
592 | pos += 3; | |
593 | args.push_back("and"); | |
594 | return true; | |
595 | } | |
596 | ||
597 | if (pos + 2 <= size && strncmp(str + pos, "or", 2) == 0) { | |
598 | pos += 2; | |
599 | args.push_back("or"); | |
600 | return true; | |
601 | } | |
602 | ||
603 | return false; | |
604 | } | |
605 | ||
606 | bool ESInfixQueryParser::parse_specific_char(const char *pchar) { | |
607 | skip_whitespace(str, size, pos); | |
608 | if (pos >= size) { | |
609 | return false; | |
610 | } | |
611 | if (str[pos] != *pchar) { | |
612 | return false; | |
613 | } | |
614 | ||
615 | args.push_back(pchar); | |
616 | ++pos; | |
617 | return true; | |
618 | } | |
619 | ||
620 | bool ESInfixQueryParser::parse_open_bracket() { | |
621 | return parse_specific_char("("); | |
622 | } | |
623 | ||
624 | bool ESInfixQueryParser::parse_close_bracket() { | |
625 | return parse_specific_char(")"); | |
626 | } | |
627 | ||
628 | bool ESInfixQueryParser::parse(list<string> *result) { | |
629 | /* | |
630 | * expression: [(]<condition>[[and/or]<condition>][)][and/or]... | |
631 | */ | |
632 | ||
633 | while (pos < size) { | |
634 | parse_open_bracket(); | |
635 | if (!parse_condition()) { | |
636 | return false; | |
637 | } | |
638 | parse_close_bracket(); | |
639 | parse_and_or(); | |
640 | } | |
641 | ||
642 | result->swap(args); | |
643 | ||
644 | return true; | |
645 | } | |
646 | ||
647 | bool ESQueryCompiler::convert(list<string>& infix, string *perr) { | |
648 | list<string> prefix; | |
649 | if (!infix_to_prefix(infix, &prefix)) { | |
650 | *perr = "invalid query"; | |
651 | return false; | |
652 | } | |
653 | stack.assign(prefix); | |
654 | if (!alloc_node(this, &stack, &query_root, perr)) { | |
655 | return false; | |
656 | } | |
657 | if (!stack.done()) { | |
658 | *perr = "invalid query"; | |
659 | return false; | |
660 | } | |
661 | return true; | |
662 | } | |
663 | ||
664 | ESQueryCompiler::~ESQueryCompiler() { | |
665 | delete query_root; | |
666 | } | |
667 | ||
668 | bool ESQueryCompiler::compile(string *perr) { | |
669 | list<string> infix; | |
670 | if (!parser.parse(&infix)) { | |
671 | *perr = "failed to parse query"; | |
672 | return false; | |
673 | } | |
674 | ||
675 | if (!convert(infix, perr)) { | |
676 | return false; | |
677 | } | |
678 | ||
679 | for (auto& c : eq_conds) { | |
680 | ESQueryNode_Op_Equal *eq_node = new ESQueryNode_Op_Equal(this, c.first, c.second); | |
681 | eq_node->set_allow_restricted(true); /* can access restricted fields */ | |
682 | ESQueryNode *effective_node; | |
683 | if (!eq_node->init(nullptr, &effective_node, perr)) { | |
684 | delete eq_node; | |
685 | return false; | |
686 | } | |
687 | query_root = new ESQueryNode_Bool(this, "and", effective_node, query_root); | |
688 | } | |
689 | ||
690 | return true; | |
691 | } | |
692 | ||
693 | void ESQueryCompiler::dump(Formatter *f) const { | |
694 | encode_json("query", *query_root, f); | |
695 | } | |
696 |