1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
8 #include <boost/algorithm/string.hpp>
10 #include "common/ceph_json.h"
11 #include "rgw_common.h"
12 #include "rgw_es_query.h"
15 #define dout_context g_ceph_context
16 #define dout_subsys ceph_subsys_rgw
18 bool pop_front(list
<string
>& l
, string
*s
)
28 map
<string
, int> operator_map
= {
39 bool is_operator(const string
& s
)
41 return (operator_map
.find(s
) != operator_map
.end());
44 int operand_value(const string
& op
)
46 auto i
= operator_map
.find(op
);
47 if (i
== operator_map
.end()) {
54 int check_precedence(const string
& op1
, const string
& op2
)
56 return operand_value(op1
) - operand_value(op2
);
59 static bool infix_to_prefix(list
<string
>& source
, list
<string
> *out
)
61 list
<string
> operator_stack
;
62 list
<string
> operand_stack
;
64 operator_stack
.push_front("(");
65 source
.push_back(")");
67 for (string
& entity
: source
) {
69 operator_stack
.push_front(entity
);
70 } else if (entity
== ")") {
71 string popped_operator
;
72 if (!pop_front(operator_stack
, &popped_operator
)) {
76 while (popped_operator
!= "(") {
77 operand_stack
.push_front(popped_operator
);
78 if (!pop_front(operator_stack
, &popped_operator
)) {
83 } else if (is_operator(entity
)) {
84 string popped_operator
;
85 if (!pop_front(operator_stack
, &popped_operator
)) {
89 int precedence
= check_precedence(popped_operator
, entity
);
91 while (precedence
>= 0) {
92 operand_stack
.push_front(popped_operator
);
93 if (!pop_front(operator_stack
, &popped_operator
)) {
96 precedence
= check_precedence(popped_operator
, entity
);
99 operator_stack
.push_front(popped_operator
);
100 operator_stack
.push_front(entity
);
102 operand_stack
.push_front(entity
);
107 if (!operator_stack
.empty()) {
111 out
->swap(operand_stack
);
117 ESQueryCompiler
*compiler
;
119 ESQueryNode(ESQueryCompiler
*_compiler
) : compiler(_compiler
) {}
120 virtual ~ESQueryNode() {}
122 virtual bool init(ESQueryStack
*s
, ESQueryNode
**pnode
, string
*perr
) = 0;
124 virtual void dump(Formatter
*f
) const = 0;
127 static bool alloc_node(ESQueryCompiler
*compiler
, ESQueryStack
*s
, ESQueryNode
**pnode
, string
*perr
);
129 class ESQueryNode_Bool
: public ESQueryNode
{
131 ESQueryNode
*first
{nullptr};
132 ESQueryNode
*second
{nullptr};
134 explicit ESQueryNode_Bool(ESQueryCompiler
*compiler
) : ESQueryNode(compiler
) {}
135 ESQueryNode_Bool(ESQueryCompiler
*compiler
, const string
& _op
, ESQueryNode
*_first
, ESQueryNode
*_second
) :ESQueryNode(compiler
), op(_op
), first(_first
), second(_second
) {}
136 bool init(ESQueryStack
*s
, ESQueryNode
**pnode
, string
*perr
) override
{
137 bool valid
= s
->pop(&op
);
139 *perr
= "incorrect expression";
142 valid
= alloc_node(compiler
, s
, &first
, perr
) &&
143 alloc_node(compiler
, s
, &second
, perr
);
150 virtual ~ESQueryNode_Bool() {
155 void dump(Formatter
*f
) const override
{
156 f
->open_object_section("bool");
157 const char *section
= (op
== "and" ? "must" : "should");
158 f
->open_array_section(section
);
159 encode_json("entry", *first
, f
);
160 encode_json("entry", *second
, f
);
167 class ESQueryNodeLeafVal
{
169 ESQueryNodeLeafVal() = default;
170 virtual ~ESQueryNodeLeafVal() {}
172 virtual bool init(const string
& str_val
, string
*perr
) = 0;
173 virtual void encode_json(const string
& field
, Formatter
*f
) const = 0;
176 class ESQueryNodeLeafVal_Str
: public ESQueryNodeLeafVal
{
179 ESQueryNodeLeafVal_Str() {}
180 bool init(const string
& str_val
, string
*perr
) override
{
184 void encode_json(const string
& field
, Formatter
*f
) const override
{
185 ::encode_json(field
.c_str(), val
.c_str(), f
);
189 class ESQueryNodeLeafVal_Int
: public ESQueryNodeLeafVal
{
192 ESQueryNodeLeafVal_Int() {}
193 bool init(const string
& str_val
, string
*perr
) override
{
195 val
= strict_strtoll(str_val
.c_str(), 10, &err
);
197 *perr
= string("failed to parse integer: ") + err
;
202 void encode_json(const string
& field
, Formatter
*f
) const override
{
203 ::encode_json(field
.c_str(), val
, f
);
207 class ESQueryNodeLeafVal_Date
: public ESQueryNodeLeafVal
{
210 ESQueryNodeLeafVal_Date() {}
211 bool init(const string
& str_val
, string
*perr
) override
{
212 if (parse_time(str_val
.c_str(), &val
) < 0) {
213 *perr
= string("failed to parse date: ") + str_val
;
218 void encode_json(const string
& field
, Formatter
*f
) const override
{
220 rgw_to_iso8601(val
, &s
);
221 ::encode_json(field
.c_str(), s
, f
);
225 class ESQueryNode_Op
: public ESQueryNode
{
230 ESQueryNodeLeafVal
*val
{nullptr};
231 ESEntityTypeMap::EntityType entity_type
{ESEntityTypeMap::ES_ENTITY_NONE
};
232 bool allow_restricted
{false};
234 bool val_from_str(string
*perr
) {
235 switch (entity_type
) {
236 case ESEntityTypeMap::ES_ENTITY_DATE
:
237 val
= new ESQueryNodeLeafVal_Date
;
239 case ESEntityTypeMap::ES_ENTITY_INT
:
240 val
= new ESQueryNodeLeafVal_Int
;
243 val
= new ESQueryNodeLeafVal_Str
;
245 return val
->init(str_val
, perr
);
247 bool do_init(ESQueryNode
**pnode
, string
*perr
) {
248 field
= compiler
->unalias_field(field
);
249 ESQueryNode
*effective_node
;
250 if (!handle_nested(&effective_node
, perr
)) {
253 if (!val_from_str(perr
)) {
256 *pnode
= effective_node
;
261 ESQueryNode_Op(ESQueryCompiler
*compiler
) : ESQueryNode(compiler
) {}
265 virtual bool init(ESQueryStack
*s
, ESQueryNode
**pnode
, string
*perr
) override
{
266 bool valid
= s
->pop(&op
) &&
270 *perr
= "invalid expression";
273 return do_init(pnode
, perr
);
275 bool handle_nested(ESQueryNode
**pnode
, string
*perr
);
277 void set_allow_restricted(bool allow
) {
278 allow_restricted
= allow
;
281 virtual void dump(Formatter
*f
) const override
= 0;
284 class ESQueryNode_Op_Equal
: public ESQueryNode_Op
{
286 explicit ESQueryNode_Op_Equal(ESQueryCompiler
*compiler
) : ESQueryNode_Op(compiler
) {}
287 ESQueryNode_Op_Equal(ESQueryCompiler
*compiler
, const string
& f
, const string
& v
) : ESQueryNode_Op(compiler
) {
293 bool init(ESQueryStack
*s
, ESQueryNode
**pnode
, string
*perr
) override
{
295 return ESQueryNode_Op::init(s
, pnode
, perr
);
297 return do_init(pnode
, perr
);
300 virtual void dump(Formatter
*f
) const override
{
301 f
->open_object_section("term");
302 val
->encode_json(field
, f
);
307 class ESQueryNode_Op_NotEqual
: public ESQueryNode_Op
{
309 explicit ESQueryNode_Op_NotEqual(ESQueryCompiler
*compiler
) : ESQueryNode_Op(compiler
) {}
310 ESQueryNode_Op_NotEqual(ESQueryCompiler
*compiler
, const string
& f
, const string
& v
) : ESQueryNode_Op(compiler
) {
316 bool init(ESQueryStack
*s
, ESQueryNode
**pnode
, string
*perr
) override
{
318 return ESQueryNode_Op::init(s
, pnode
, perr
);
320 return do_init(pnode
, perr
);
323 virtual void dump(Formatter
*f
) const override
{
324 f
->open_object_section("bool");
325 f
->open_object_section("must_not");
326 f
->open_object_section("term");
327 val
->encode_json(field
, f
);
334 class ESQueryNode_Op_Range
: public ESQueryNode_Op
{
337 ESQueryNode_Op_Range(ESQueryCompiler
*compiler
, const string
& rs
) : ESQueryNode_Op(compiler
), range_str(rs
) {}
339 virtual void dump(Formatter
*f
) const override
{
340 f
->open_object_section("range");
341 f
->open_object_section(field
.c_str());
342 val
->encode_json(range_str
, f
);
348 class ESQueryNode_Op_Nested_Parent
: public ESQueryNode_Op
{
350 ESQueryNode_Op_Nested_Parent(ESQueryCompiler
*compiler
) : ESQueryNode_Op(compiler
) {}
352 virtual string
get_custom_leaf_field_name() = 0;
356 class ESQueryNode_Op_Nested
: public ESQueryNode_Op_Nested_Parent
{
360 ESQueryNode_Op_Nested(ESQueryCompiler
*compiler
, const string
& _name
, ESQueryNode
*_next
) : ESQueryNode_Op_Nested_Parent(compiler
),
361 name(_name
), next(_next
) {}
362 ~ESQueryNode_Op_Nested() {
366 virtual void dump(Formatter
*f
) const override
{
367 f
->open_object_section("nested");
368 string s
= string("meta.custom-") + type_str();
369 encode_json("path", s
.c_str(), f
);
370 f
->open_object_section("query");
371 f
->open_object_section("bool");
372 f
->open_array_section("must");
373 f
->open_object_section("entry");
374 f
->open_object_section("match");
375 string n
= s
+ ".name";
376 encode_json(n
.c_str(), name
.c_str(), f
);
379 encode_json("entry", *next
, f
);
386 string
type_str() const;
387 string
get_custom_leaf_field_name() override
{
388 return string("meta.custom-") + type_str() + ".value";
393 string ESQueryNode_Op_Nested
<string
>::type_str() const {
398 string ESQueryNode_Op_Nested
<int64_t>::type_str() const {
403 string ESQueryNode_Op_Nested
<ceph::real_time
>::type_str() const {
407 bool ESQueryNode_Op::handle_nested(ESQueryNode
**pnode
, string
*perr
)
409 string field_name
= field
;
410 const string
& custom_prefix
= compiler
->get_custom_prefix();
411 if (!boost::algorithm::starts_with(field_name
, custom_prefix
)) {
413 auto m
= compiler
->get_generic_type_map();
415 bool found
= m
->find(field_name
, &entity_type
) &&
416 (allow_restricted
|| !compiler
->is_restricted(field_name
));
418 *perr
= string("unexpected generic field '") + field_name
+ "'";
422 *perr
= "query parser does not support generic types";
426 field_name
= field_name
.substr(custom_prefix
.size());
427 auto m
= compiler
->get_custom_type_map();
429 m
->find(field_name
, &entity_type
);
430 /* ignoring returned bool, for now just treat it as string */
433 ESQueryNode_Op_Nested_Parent
*new_node
;
434 switch (entity_type
) {
435 case ESEntityTypeMap::ES_ENTITY_INT
:
436 new_node
= new ESQueryNode_Op_Nested
<int64_t>(compiler
, field_name
, this);
438 case ESEntityTypeMap::ES_ENTITY_DATE
:
439 new_node
= new ESQueryNode_Op_Nested
<ceph::real_time
>(compiler
, field_name
, this);
442 new_node
= new ESQueryNode_Op_Nested
<string
>(compiler
, field_name
, this);
445 field
= new_node
->get_custom_leaf_field_name();
451 static bool is_bool_op(const string
& str
)
453 return (str
== "or" || str
== "and");
456 static bool alloc_node(ESQueryCompiler
*compiler
, ESQueryStack
*s
, ESQueryNode
**pnode
, string
*perr
)
459 bool valid
= s
->peek(&op
);
461 *perr
= "incorrect expression";
467 if (is_bool_op(op
)) {
468 node
= new ESQueryNode_Bool(compiler
);
469 } else if (op
== "==") {
470 node
= new ESQueryNode_Op_Equal(compiler
);
471 } else if (op
== "!=") {
472 node
= new ESQueryNode_Op_NotEqual(compiler
);
474 static map
<string
, string
> range_op_map
= {
481 auto iter
= range_op_map
.find(op
);
482 if (iter
== range_op_map
.end()) {
483 *perr
= string("invalid operator: ") + op
;
487 node
= new ESQueryNode_Op_Range(compiler
, iter
->second
);
490 if (!node
->init(s
, pnode
, perr
)) {
498 bool is_key_char(char c
)
523 return (isascii(c
) > 0);
526 static bool is_op_char(char c
)
538 static bool is_val_char(char c
)
546 void ESInfixQueryParser::skip_whitespace(const char *str
, int size
, int& pos
) {
547 while (pos
< size
&& isspace(str
[pos
])) {
552 bool ESInfixQueryParser::get_next_token(bool (*filter
)(char)) {
553 skip_whitespace(str
, size
, pos
);
554 int token_start
= pos
;
555 while (pos
< size
&& filter(str
[pos
])) {
558 if (pos
== token_start
) {
561 string token
= string(str
+ token_start
, pos
- token_start
);
562 args
.push_back(token
);
566 bool ESInfixQueryParser::parse_condition() {
568 * condition: <key> <operator> <val>
570 * whereas key: needs to conform to http header field restrictions
571 * operator: one of the following: < <= == != >= >
572 * val: ascii, terminated by either space or ')' (or end of string)
576 bool valid
= get_next_token(is_key_char
) &&
577 get_next_token(is_op_char
) &&
578 get_next_token(is_val_char
);
587 bool ESInfixQueryParser::parse_and_or() {
588 skip_whitespace(str
, size
, pos
);
589 if (pos
+ 3 <= size
&& strncmp(str
+ pos
, "and", 3) == 0) {
591 args
.push_back("and");
595 if (pos
+ 2 <= size
&& strncmp(str
+ pos
, "or", 2) == 0) {
597 args
.push_back("or");
604 bool ESInfixQueryParser::parse_specific_char(const char *pchar
) {
605 skip_whitespace(str
, size
, pos
);
609 if (str
[pos
] != *pchar
) {
613 args
.push_back(pchar
);
618 bool ESInfixQueryParser::parse_open_bracket() {
619 return parse_specific_char("(");
622 bool ESInfixQueryParser::parse_close_bracket() {
623 return parse_specific_char(")");
626 bool ESInfixQueryParser::parse(list
<string
> *result
) {
628 * expression: [(]<condition>[[and/or]<condition>][)][and/or]...
632 parse_open_bracket();
633 if (!parse_condition()) {
636 parse_close_bracket();
645 bool ESQueryCompiler::convert(list
<string
>& infix
, string
*perr
) {
647 if (!infix_to_prefix(infix
, &prefix
)) {
648 *perr
= "invalid query";
651 stack
.assign(prefix
);
652 if (!alloc_node(this, &stack
, &query_root
, perr
)) {
656 *perr
= "invalid query";
662 ESQueryCompiler::~ESQueryCompiler() {
666 bool ESQueryCompiler::compile(string
*perr
) {
668 if (!parser
.parse(&infix
)) {
669 *perr
= "failed to parse query";
673 if (!convert(infix
, perr
)) {
677 for (auto& c
: eq_conds
) {
678 ESQueryNode_Op_Equal
*eq_node
= new ESQueryNode_Op_Equal(this, c
.first
, c
.second
);
679 eq_node
->set_allow_restricted(true); /* can access restricted fields */
680 ESQueryNode
*effective_node
;
681 if (!eq_node
->init(nullptr, &effective_node
, perr
)) {
685 query_root
= new ESQueryNode_Bool(this, "and", effective_node
, query_root
);
691 void ESQueryCompiler::dump(Formatter
*f
) const {
692 encode_json("query", *query_root
, f
);