Skip to content

Commit 1ff8b77

Browse files
authored
Add support for KQL not() (#1188)
Noticed we have the `LogicalExpression` support for `not()` (used in conjunction with `where`) but never added this standalone in the KQL grammar
1 parent e07dd86 commit 1ff8b77

12 files changed

+192
-62
lines changed

rust/experimental/query_engine/kql-parser/src/kql.pest

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ regex_expression = { "regex(" ~ string_literal ~ ("," ~ string_literal)? ~ ")" }
127127
dynamic_array_expression = { "[" ~ (dynamic_inner_expression ~ ("," ~ dynamic_inner_expression)*)? ~ "]" }
128128
dynamic_map_item_expression = { string_literal ~ ":" ~ dynamic_inner_expression }
129129
dynamic_map_expression = { "{" ~ (dynamic_map_item_expression ~ ("," ~ dynamic_map_item_expression)*)? ~ "}" }
130-
dynamic_inner_expression = _{ dynamic_array_expression|dynamic_map_expression|type_expressions }
130+
dynamic_inner_expression = _{ dynamic_array_expression|dynamic_map_expression|type_unary_expressions }
131131
dynamic_expression = { "dynamic" ~ "(" ~ dynamic_inner_expression ~ ")" }
132-
type_expressions = {
132+
type_unary_expressions = {
133133
null_literal
134134
| real_expression
135135
| datetime_expression
@@ -146,7 +146,7 @@ type_expressions = {
146146
conditional_expression = { ("iff"|"iif") ~ "(" ~ logical_expression ~ "," ~ scalar_expression ~ "," ~ scalar_expression ~ ")" }
147147
case_expression = { "case" ~ "(" ~ logical_expression ~ "," ~ scalar_expression ~ ("," ~ logical_expression ~ "," ~ scalar_expression)* ~ "," ~ scalar_expression ~ ")" }
148148
coalesce_expression = { "coalesce" ~ "(" ~ scalar_expression ~ "," ~ scalar_expression ~ ("," ~ scalar_expression)* ~ ")" }
149-
conditional_expressions = {
149+
conditional_unary_expressions = {
150150
conditional_expression
151151
| case_expression
152152
| coalesce_expression
@@ -161,7 +161,7 @@ toreal_expression = { "toreal" ~ "(" ~ scalar_expression ~ ")" }
161161
todouble_expression = { "todouble" ~ "(" ~ scalar_expression ~ ")" }
162162
todatetime_expression = { "todatetime" ~ "(" ~ scalar_expression ~ ")" }
163163
totimespan_expression = { "totimespan" ~ "(" ~ scalar_expression ~ ")" }
164-
conversion_expressions = {
164+
conversion_unary_expressions = {
165165
tostring_expression
166166
| toint_expression
167167
| tobool_expression
@@ -179,7 +179,7 @@ substring_expression = { "substring" ~ "(" ~ scalar_expression ~ "," ~ scalar_ex
179179
strcat_expression = { "strcat" ~ scalar_list_expression }
180180
strcat_delim_expression = { "strcat_delim" ~ "(" ~ scalar_expression ~ "," ~ scalar_expression ~ ("," ~ scalar_expression)* ~ ")" }
181181
extract_expression = { "extract" ~ "(" ~ scalar_expression ~ "," ~ scalar_expression ~ "," ~ scalar_expression ~ ")" }
182-
string_expressions = {
182+
string_unary_expressions = {
183183
strlen_expression
184184
| replace_string_expression
185185
| substring_expression
@@ -190,41 +190,47 @@ string_expressions = {
190190

191191
parse_json_expression = { "parse_json" ~ "(" ~ scalar_expression ~ ")" }
192192
parse_regex_expression = { "parse_regex" ~ "(" ~ scalar_expression ~ ("," ~ scalar_expression)? ~ ")" }
193-
parse_expressions = {
193+
parse_unary_expressions = {
194194
parse_json_expression
195195
| parse_regex_expression
196196
}
197197

198198
array_concat_expression = { "array_concat" ~ scalar_list_expression }
199-
array_expressions = {
199+
array_unary_expressions = {
200200
array_concat_expression
201201
}
202202

203203
negate_expression = { "-" ~ scalar_unary_expression }
204204
bin_expression = { "bin" ~ "(" ~ scalar_expression ~ "," ~ scalar_expression ~ ")" }
205-
math_expressions = {
205+
math_unary_expressions = {
206206
negate_expression
207207
| bin_expression
208208
}
209209

210210
now_expression = { "now" ~ "(" ~ scalar_expression? ~ ")" }
211-
temporal_expressions = {
211+
temporal_unary_expressions = {
212212
now_expression
213213
}
214214

215+
not_expression = { "not" ~ "(" ~ logical_expression ~ ")" }
216+
logical_unary_expressions = {
217+
not_expression
218+
}
219+
215220
/* Note: Order is imporant here. Once Pest has matched something it won't go
216221
backwards. For example if integer_literal is defined before time_expression "1h"
217222
would be parsed as integer_literal(1) and the remaining "h" would be fed into
218223
the next rule. */
219224
scalar_unary_expression = {
220-
type_expressions
221-
| conditional_expressions
222-
| conversion_expressions
223-
| string_expressions
224-
| array_expressions
225-
| math_expressions
226-
| temporal_expressions
227-
| parse_expressions
225+
type_unary_expressions
226+
| conditional_unary_expressions
227+
| conversion_unary_expressions
228+
| string_unary_expressions
229+
| array_unary_expressions
230+
| math_unary_expressions
231+
| temporal_unary_expressions
232+
| logical_unary_expressions
233+
| parse_unary_expressions
228234
| accessor_expression
229235
| "(" ~ scalar_expression ~ ")"
230236
}

rust/experimental/query_engine/kql-parser/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub(crate) mod scalar_array_function_expressions;
1010
pub(crate) mod scalar_conditional_function_expressions;
1111
pub(crate) mod scalar_conversion_function_expressions;
1212
pub(crate) mod scalar_expression;
13+
pub(crate) mod scalar_logical_function_expressions;
1314
pub(crate) mod scalar_mathematical_function_expressions;
1415
pub(crate) mod scalar_parse_function_expressions;
1516
pub(crate) mod scalar_primitive_expressions;

rust/experimental/query_engine/kql-parser/src/scalar_array_function_expressions.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ use pest::iterators::Pair;
77

88
use crate::{Rule, scalar_expression::parse_scalar_expression};
99

10-
pub(crate) fn parse_array_expressions(
11-
array_expressions_rule: Pair<Rule>,
10+
pub(crate) fn parse_array_unary_expressions(
11+
array_unary_expressions_rule: Pair<Rule>,
1212
scope: &dyn ParserScope,
1313
) -> Result<ScalarExpression, ParserError> {
14-
let rule = array_expressions_rule.into_inner().next().unwrap();
14+
let rule = array_unary_expressions_rule.into_inner().next().unwrap();
1515

1616
match rule.as_rule() {
1717
Rule::array_concat_expression => parse_array_concat_expression(rule, scope),
18-
_ => panic!("Unexpected rule in array_expressions: {rule}"),
18+
_ => panic!("Unexpected rule in array_unary_expressions: {rule}"),
1919
}
2020
}
2121

rust/experimental/query_engine/kql-parser/src/scalar_conditional_function_expressions.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@ use crate::{
99
Rule, logical_expressions::parse_logical_expression, scalar_expression::parse_scalar_expression,
1010
};
1111

12-
pub(crate) fn parse_conditional_expressions(
13-
conditional_expressions_rule: Pair<Rule>,
12+
pub(crate) fn parse_conditional_unary_expressions(
13+
conditional_unary_expressions_rule: Pair<Rule>,
1414
scope: &dyn ParserScope,
1515
) -> Result<ScalarExpression, ParserError> {
16-
let rule = conditional_expressions_rule.into_inner().next().unwrap();
16+
let rule = conditional_unary_expressions_rule
17+
.into_inner()
18+
.next()
19+
.unwrap();
1720

1821
match rule.as_rule() {
1922
Rule::conditional_expression => parse_conditional_expression(rule, scope),
2023
Rule::case_expression => parse_case_expression(rule, scope),
2124
Rule::coalesce_expression => parse_coalesce_expression(rule, scope),
22-
_ => panic!("Unexpected rule in conditional_expressions: {rule}"),
25+
_ => panic!("Unexpected rule in conditional_unary_expressions: {rule}"),
2326
}
2427
}
2528

rust/experimental/query_engine/kql-parser/src/scalar_conversion_function_expressions.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ use pest::iterators::Pair;
77

88
use crate::{Rule, scalar_expression::parse_scalar_expression};
99

10-
pub(crate) fn parse_conversion_expressions(
11-
conversion_expressions_rule: Pair<Rule>,
10+
pub(crate) fn parse_conversion_unary_expressions(
11+
conversion_unary_expressions_rule: Pair<Rule>,
1212
scope: &dyn ParserScope,
1313
) -> Result<ScalarExpression, ParserError> {
14-
let rule = conversion_expressions_rule.into_inner().next().unwrap();
14+
let rule = conversion_unary_expressions_rule
15+
.into_inner()
16+
.next()
17+
.unwrap();
1518

1619
match rule.as_rule() {
1720
Rule::tostring_expression => parse_tostring_expression(rule, scope),
@@ -23,7 +26,7 @@ pub(crate) fn parse_conversion_expressions(
2326
Rule::todouble_expression => parse_todouble_expression(rule, scope),
2427
Rule::todatetime_expression => parse_todatetime_expression(rule, scope),
2528
Rule::totimespan_expression => parse_totimespan_expression(rule, scope),
26-
_ => panic!("Unexpected rule in conversion_expressions: {rule}"),
29+
_ => panic!("Unexpected rule in conversion_unary_expressions: {rule}"),
2730
}
2831
}
2932

rust/experimental/query_engine/kql-parser/src/scalar_expression.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use pest::{iterators::Pair, pratt_parser::*};
1010
use crate::{
1111
Rule, logical_expressions::to_logical_expression, scalar_array_function_expressions::*,
1212
scalar_conditional_function_expressions::*, scalar_conversion_function_expressions::*,
13-
scalar_mathematical_function_expressions::*, scalar_parse_function_expressions::*,
14-
scalar_primitive_expressions::*, scalar_string_function_expressions::*,
15-
scalar_temporal_function_expressions::*,
13+
scalar_logical_function_expressions::*, scalar_mathematical_function_expressions::*,
14+
scalar_parse_function_expressions::*, scalar_primitive_expressions::*,
15+
scalar_string_function_expressions::*, scalar_temporal_function_expressions::*,
1616
};
1717

1818
static PRATT_PARSER: LazyLock<PrattParser<Rule>> = LazyLock::new(|| {
@@ -285,14 +285,17 @@ pub(crate) fn parse_scalar_unary_expression(
285285
let rule = scalar_unary_expression_rule.into_inner().next().unwrap();
286286

287287
Ok(match rule.as_rule() {
288-
Rule::type_expressions => ScalarExpression::Static(parse_type_expressions(rule)?),
289-
Rule::conditional_expressions => parse_conditional_expressions(rule, scope)?,
290-
Rule::conversion_expressions => parse_conversion_expressions(rule, scope)?,
291-
Rule::string_expressions => parse_string_expressions(rule, scope)?,
292-
Rule::parse_expressions => parse_parse_expressions(rule, scope)?,
293-
Rule::array_expressions => parse_array_expressions(rule, scope)?,
294-
Rule::math_expressions => parse_math_expressions(rule, scope)?,
295-
Rule::temporal_expressions => parse_temporal_expressions(rule, scope)?,
288+
Rule::type_unary_expressions => {
289+
ScalarExpression::Static(parse_type_unary_expressions(rule)?)
290+
}
291+
Rule::conditional_unary_expressions => parse_conditional_unary_expressions(rule, scope)?,
292+
Rule::conversion_unary_expressions => parse_conversion_unary_expressions(rule, scope)?,
293+
Rule::string_unary_expressions => parse_string_unary_expressions(rule, scope)?,
294+
Rule::parse_unary_expressions => parse_parse_unary_expressions(rule, scope)?,
295+
Rule::array_unary_expressions => parse_array_unary_expressions(rule, scope)?,
296+
Rule::math_unary_expressions => parse_math_unary_expressions(rule, scope)?,
297+
Rule::temporal_unary_expressions => parse_temporal_unary_expressions(rule, scope)?,
298+
Rule::logical_unary_expressions => parse_logical_unary_expressions(rule, scope)?,
296299
Rule::accessor_expression => {
297300
// Note: When used as a scalar expression it is valid for an
298301
// accessor to fold into a static at the root so
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use data_engine_expressions::*;
5+
use data_engine_parser_abstractions::*;
6+
use pest::iterators::Pair;
7+
8+
use crate::{Rule, logical_expressions::parse_logical_expression};
9+
10+
pub(crate) fn parse_logical_unary_expressions(
11+
logical_unary_expressions_rule: Pair<Rule>,
12+
scope: &dyn ParserScope,
13+
) -> Result<ScalarExpression, ParserError> {
14+
let rule = logical_unary_expressions_rule.into_inner().next().unwrap();
15+
16+
match rule.as_rule() {
17+
Rule::not_expression => parse_not_expression(rule, scope),
18+
_ => panic!("Unexpected rule in logical_unary_expressions: {rule}"),
19+
}
20+
}
21+
22+
fn parse_not_expression(
23+
not_expression_rule: Pair<Rule>,
24+
scope: &dyn ParserScope,
25+
) -> Result<ScalarExpression, ParserError> {
26+
let query_location = to_query_location(&not_expression_rule);
27+
28+
let mut not_rules = not_expression_rule.into_inner();
29+
let logical_expr_rule = not_rules.next().unwrap();
30+
31+
let inner_logical = parse_logical_expression(logical_expr_rule, scope)?;
32+
33+
Ok(ScalarExpression::Logical(
34+
LogicalExpression::Not(NotLogicalExpression::new(query_location, inner_logical)).into(),
35+
))
36+
}
37+
38+
#[cfg(test)]
39+
mod tests {
40+
use super::*;
41+
use crate::KqlPestParser;
42+
use pest::Parser;
43+
44+
#[test]
45+
fn test_pest_parse_not_expression_rule() {
46+
pest_test_helpers::test_pest_rule::<KqlPestParser, Rule>(
47+
Rule::not_expression,
48+
&[
49+
"not(true)",
50+
"not(false)",
51+
"not(1 == 1)",
52+
"not(field contains 'value')",
53+
"not(x > 5 and y < 10)",
54+
],
55+
&[
56+
"not()", // missing argument
57+
"not(1, 2)", // too many arguments
58+
"not true", // missing parentheses
59+
],
60+
);
61+
}
62+
63+
#[test]
64+
fn test_parse_not_expression() {
65+
let run_test = |input: &str, expected: LogicalExpression| {
66+
println!("Testing: {input}");
67+
68+
let state = ParserState::new_with_options(
69+
input,
70+
ParserOptions::new().with_attached_data_names(&["resource"]),
71+
);
72+
73+
let mut result = KqlPestParser::parse(Rule::not_expression, input).unwrap();
74+
75+
let result = parse_not_expression(result.next().unwrap(), &state).unwrap();
76+
77+
let expected_scalar = ScalarExpression::Logical(expected.into());
78+
79+
assert_eq!(result, expected_scalar);
80+
};
81+
82+
run_test(
83+
"not(true)",
84+
LogicalExpression::Not(NotLogicalExpression::new(
85+
QueryLocation::new_fake(),
86+
LogicalExpression::Scalar(ScalarExpression::Static(
87+
StaticScalarExpression::Boolean(BooleanScalarExpression::new(
88+
QueryLocation::new_fake(),
89+
true,
90+
)),
91+
)),
92+
)),
93+
);
94+
95+
run_test(
96+
"not(1 == 2)",
97+
LogicalExpression::Not(NotLogicalExpression::new(
98+
QueryLocation::new_fake(),
99+
LogicalExpression::EqualTo(EqualToLogicalExpression::new(
100+
QueryLocation::new_fake(),
101+
ScalarExpression::Static(StaticScalarExpression::Integer(
102+
IntegerScalarExpression::new(QueryLocation::new_fake(), 1),
103+
)),
104+
ScalarExpression::Static(StaticScalarExpression::Integer(
105+
IntegerScalarExpression::new(QueryLocation::new_fake(), 2),
106+
)),
107+
false,
108+
)),
109+
)),
110+
);
111+
}
112+
}

rust/experimental/query_engine/kql-parser/src/scalar_mathematical_function_expressions.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ use pest::iterators::Pair;
77

88
use crate::{Rule, scalar_expression::*};
99

10-
pub(crate) fn parse_math_expressions(
11-
math_expressions_rule: Pair<Rule>,
10+
pub(crate) fn parse_math_unary_expressions(
11+
math_unary_expressions_rule: Pair<Rule>,
1212
scope: &dyn ParserScope,
1313
) -> Result<ScalarExpression, ParserError> {
14-
let rule = math_expressions_rule.into_inner().next().unwrap();
14+
let rule = math_unary_expressions_rule.into_inner().next().unwrap();
1515

1616
match rule.as_rule() {
1717
Rule::negate_expression => parse_negate_expression(rule, scope),
1818
Rule::bin_expression => parse_bin_expression(rule, scope),
19-
_ => panic!("Unexpected rule in math_expressions: {rule}"),
19+
_ => panic!("Unexpected rule in math_unary_expressions: {rule}"),
2020
}
2121
}
2222

rust/experimental/query_engine/kql-parser/src/scalar_parse_function_expressions.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ use pest::iterators::Pair;
77

88
use crate::{Rule, scalar_expression::parse_scalar_expression};
99

10-
pub(crate) fn parse_parse_expressions(
11-
parse_expressions_rule: Pair<Rule>,
10+
pub(crate) fn parse_parse_unary_expressions(
11+
parse_unary_expressions_rule: Pair<Rule>,
1212
scope: &dyn ParserScope,
1313
) -> Result<ScalarExpression, ParserError> {
14-
let rule = parse_expressions_rule.into_inner().next().unwrap();
14+
let rule = parse_unary_expressions_rule.into_inner().next().unwrap();
1515

1616
match rule.as_rule() {
1717
Rule::parse_json_expression => parse_parse_json_expression(rule, scope),
1818
Rule::parse_regex_expression => parse_parse_regex_expression(rule, scope),
19-
_ => panic!("Unexpected rule in parse_expressions: {rule}"),
19+
_ => panic!("Unexpected rule in parse_unary_expressions: {rule}"),
2020
}
2121
}
2222

0 commit comments

Comments
 (0)