]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_es_query.cc
import ceph quincy 17.2.6
[ceph.git] / ceph / src / rgw / rgw_es_query.cc
CommitLineData
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
18using namespace std;
19
31f18b77
FG
20bool 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
30map<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
41bool is_operator(const string& s)
42{
43 return (operator_map.find(s) != operator_map.end());
44}
45
46int 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
56int check_precedence(const string& op1, const string& op2)
57{
58 return operand_value(op1) - operand_value(op2);
59}
60
61static 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
117class ESQueryNode {
118protected:
119 ESQueryCompiler *compiler;
120public:
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
129static bool alloc_node(ESQueryCompiler *compiler, ESQueryStack *s, ESQueryNode **pnode, string *perr);
130
131class ESQueryNode_Bool : public ESQueryNode {
132 string op;
133 ESQueryNode *first{nullptr};
134 ESQueryNode *second{nullptr};
135public:
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
169class ESQueryNodeLeafVal {
170public:
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
178class ESQueryNodeLeafVal_Str : public ESQueryNodeLeafVal {
179 string val;
180public:
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
191class ESQueryNodeLeafVal_Int : public ESQueryNodeLeafVal {
224ce89b 192 int64_t val{0};
31f18b77
FG
193public:
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
209class ESQueryNodeLeafVal_Date : public ESQueryNodeLeafVal {
210 ceph::real_time val;
211public:
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
227class ESQueryNode_Op : public ESQueryNode {
228protected:
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
262public:
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
286class ESQueryNode_Op_Equal : public ESQueryNode_Op {
287public:
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
309class ESQueryNode_Op_NotEqual : public ESQueryNode_Op {
310public:
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
336class ESQueryNode_Op_Range : public ESQueryNode_Op {
337 string range_str;
338public:
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
350class ESQueryNode_Op_Nested_Parent : public ESQueryNode_Op {
351public:
352 ESQueryNode_Op_Nested_Parent(ESQueryCompiler *compiler) : ESQueryNode_Op(compiler) {}
353
354 virtual string get_custom_leaf_field_name() = 0;
355};
356
357template <class T>
358class ESQueryNode_Op_Nested : public ESQueryNode_Op_Nested_Parent {
359 string name;
360 ESQueryNode *next;
361public:
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
394template<>
395string ESQueryNode_Op_Nested<string>::type_str() const {
396 return "string";
397}
398
399template<>
400string ESQueryNode_Op_Nested<int64_t>::type_str() const {
401 return "int";
402}
403
404template<>
405string ESQueryNode_Op_Nested<ceph::real_time>::type_str() const {
406 return "date";
407}
408
409bool 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
453static bool is_bool_op(const string& str)
454{
455 return (str == "or" || str == "and");
456}
457
458static 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
500bool 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
528static 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
540static bool is_val_char(char c)
541{
542 if (isspace(c)) {
543 return false;
544 }
545 return (c != ')');
546}
547
548void ESInfixQueryParser::skip_whitespace(const char *str, int size, int& pos) {
549 while (pos < size && isspace(str[pos])) {
550 ++pos;
551 }
552}
553
554bool 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
568bool 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
589bool 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
606bool 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
620bool ESInfixQueryParser::parse_open_bracket() {
621 return parse_specific_char("(");
622}
623
624bool ESInfixQueryParser::parse_close_bracket() {
625 return parse_specific_char(")");
626}
627
628bool 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
647bool 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
664ESQueryCompiler::~ESQueryCompiler() {
665 delete query_root;
666}
667
668bool 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
693void ESQueryCompiler::dump(Formatter *f) const {
694 encode_json("query", *query_root, f);
695}
696