Skip to content

Commit c88f174

Browse files
authored
[query-engine] Add support for parsing KQL logical expressions (open-telemetry#578)
## Changes * Adds support for parsing KQL logical expressions into query engine expressions
1 parent 7199847 commit c88f174

File tree

5 files changed

+803
-1
lines changed

5 files changed

+803
-1
lines changed

rust/experimental/query_engine/expressions/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub(crate) mod expression;
2+
pub(crate) mod logical_expressions;
23
pub(crate) mod scalar_expressions;
34
pub(crate) mod value_accessor;
45

@@ -8,4 +9,5 @@ pub use expression::QueryLocation;
89
pub use value_accessor::ValueAccessor;
910
pub use value_accessor::ValueSelector;
1011

12+
pub use logical_expressions::*;
1113
pub use scalar_expressions::*;
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
use crate::{Expression, QueryLocation, ScalarExpression};
2+
3+
#[derive(Debug, Clone, PartialEq)]
4+
pub enum LogicalExpression {
5+
/// Resolve the boolean value for the logical expression using the inner
6+
/// scalar expression.
7+
///
8+
/// Note: To be valid the inner expression should be a
9+
/// [`ScalarExpression::Boolean`] value or a resolved
10+
/// ([`ScalarExpression::Attached`], [`ScalarExpression::Source`], or
11+
/// [`ScalarExpression::Variable`]) value which is a boolean.
12+
Scalar(ScalarExpression),
13+
14+
/// Returns true if two [`ScalarExpression`] are equal.
15+
EqualTo(EqualToLogicalExpression),
16+
17+
/// Returns true if a [`ScalarExpression`] is greater than another
18+
/// [`ScalarExpression`].
19+
GreaterThan(GreaterThanLogicalExpression),
20+
21+
/// Returns true if a [`ScalarExpression`] is greater than or equal to
22+
/// another [`ScalarExpression`].
23+
GreaterThanOrEqualTo(GreaterThanOrEqualToLogicalExpression),
24+
25+
/// Returns true if the inner logical expression returns false.
26+
Not(NotLogicalExpression),
27+
28+
/// Returns the result of a sequence of logical expressions chained using
29+
/// logical `AND(&&)` and/or `OR(||)` operations.
30+
Chain(ChainLogicalExpression),
31+
}
32+
33+
#[derive(Debug, Clone, PartialEq)]
34+
pub struct ChainLogicalExpression {
35+
query_location: QueryLocation,
36+
first_expression: Box<LogicalExpression>,
37+
chain_expressions: Vec<ChainedLogicalExpression>,
38+
}
39+
40+
impl ChainLogicalExpression {
41+
pub fn new(
42+
query_location: QueryLocation,
43+
first_expression: LogicalExpression,
44+
) -> ChainLogicalExpression {
45+
Self {
46+
query_location,
47+
first_expression: first_expression.into(),
48+
chain_expressions: Vec::new(),
49+
}
50+
}
51+
52+
pub fn push_or(&mut self, expression: LogicalExpression) {
53+
self.chain_expressions
54+
.push(ChainedLogicalExpression::Or(expression));
55+
}
56+
57+
pub fn push_and(&mut self, expression: LogicalExpression) {
58+
self.chain_expressions
59+
.push(ChainedLogicalExpression::And(expression));
60+
}
61+
62+
pub fn get_expressions(&self) -> (&LogicalExpression, &Vec<ChainedLogicalExpression>) {
63+
(&self.first_expression, &self.chain_expressions)
64+
}
65+
}
66+
67+
impl Expression for ChainLogicalExpression {
68+
fn get_query_location(&self) -> &QueryLocation {
69+
&self.query_location
70+
}
71+
}
72+
73+
#[derive(Debug, Clone, PartialEq)]
74+
pub enum ChainedLogicalExpression {
75+
Or(LogicalExpression),
76+
And(LogicalExpression),
77+
}
78+
79+
#[derive(Debug, Clone, PartialEq)]
80+
pub struct EqualToLogicalExpression {
81+
query_location: QueryLocation,
82+
left: ScalarExpression,
83+
right: ScalarExpression,
84+
}
85+
86+
impl EqualToLogicalExpression {
87+
pub fn new(
88+
query_location: QueryLocation,
89+
left: ScalarExpression,
90+
right: ScalarExpression,
91+
) -> EqualToLogicalExpression {
92+
Self {
93+
query_location,
94+
left,
95+
right,
96+
}
97+
}
98+
99+
pub fn get_left(&self) -> &ScalarExpression {
100+
&self.left
101+
}
102+
103+
pub fn get_right(&self) -> &ScalarExpression {
104+
&self.right
105+
}
106+
}
107+
108+
impl Expression for EqualToLogicalExpression {
109+
fn get_query_location(&self) -> &QueryLocation {
110+
&self.query_location
111+
}
112+
}
113+
114+
#[derive(Debug, Clone, PartialEq)]
115+
pub struct GreaterThanLogicalExpression {
116+
query_location: QueryLocation,
117+
left: ScalarExpression,
118+
right: ScalarExpression,
119+
}
120+
121+
impl GreaterThanLogicalExpression {
122+
pub fn new(
123+
query_location: QueryLocation,
124+
left: ScalarExpression,
125+
right: ScalarExpression,
126+
) -> GreaterThanLogicalExpression {
127+
Self {
128+
query_location,
129+
left,
130+
right,
131+
}
132+
}
133+
134+
pub fn get_left(&self) -> &ScalarExpression {
135+
&self.left
136+
}
137+
138+
pub fn get_right(&self) -> &ScalarExpression {
139+
&self.right
140+
}
141+
}
142+
143+
impl Expression for GreaterThanLogicalExpression {
144+
fn get_query_location(&self) -> &QueryLocation {
145+
&self.query_location
146+
}
147+
}
148+
149+
#[derive(Debug, Clone, PartialEq)]
150+
pub struct GreaterThanOrEqualToLogicalExpression {
151+
query_location: QueryLocation,
152+
left: ScalarExpression,
153+
right: ScalarExpression,
154+
}
155+
156+
impl GreaterThanOrEqualToLogicalExpression {
157+
pub fn new(
158+
query_location: QueryLocation,
159+
left: ScalarExpression,
160+
right: ScalarExpression,
161+
) -> GreaterThanOrEqualToLogicalExpression {
162+
Self {
163+
query_location,
164+
left,
165+
right,
166+
}
167+
}
168+
169+
pub fn get_left(&self) -> &ScalarExpression {
170+
&self.left
171+
}
172+
173+
pub fn get_right(&self) -> &ScalarExpression {
174+
&self.right
175+
}
176+
}
177+
178+
impl Expression for GreaterThanOrEqualToLogicalExpression {
179+
fn get_query_location(&self) -> &QueryLocation {
180+
&self.query_location
181+
}
182+
}
183+
184+
#[derive(Debug, Clone, PartialEq)]
185+
pub struct NotLogicalExpression {
186+
query_location: QueryLocation,
187+
inner_expression: Box<LogicalExpression>,
188+
}
189+
190+
impl NotLogicalExpression {
191+
pub fn new(
192+
query_location: QueryLocation,
193+
inner_expression: LogicalExpression,
194+
) -> NotLogicalExpression {
195+
Self {
196+
query_location,
197+
inner_expression: inner_expression.into(),
198+
}
199+
}
200+
201+
pub fn get_inner_expression(&self) -> &LogicalExpression {
202+
&self.inner_expression
203+
}
204+
}
205+
206+
impl Expression for NotLogicalExpression {
207+
fn get_query_location(&self) -> &QueryLocation {
208+
&self.query_location
209+
}
210+
}

rust/experimental/query_engine/expressions/src/scalar_expressions.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use chrono::{DateTime, FixedOffset};
22

3-
use crate::{Expression, QueryLocation, ValueAccessor, ValueSelector};
3+
use crate::{Expression, LogicalExpression, QueryLocation, ValueAccessor, ValueSelector};
44

55
#[derive(Debug, Clone, PartialEq)]
66
pub enum ScalarExpression {
@@ -43,6 +43,9 @@ pub enum ScalarExpression {
4343

4444
/// Negate the value returned by the inner scalar expression.
4545
Negate(NegateScalarExpression),
46+
47+
/// Boolean value returned by the inner logical expression.
48+
Logical(Box<LogicalExpression>),
4649
}
4750

4851
#[derive(Debug, Clone, PartialEq)]

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ minus_token = { "-" }
66
plus_token = { "+" }
77
positive_infinity_token = { "+inf" }
88
negative_infinity_token = { "-inf" }
9+
equals_token = @{ "==" }
10+
not_equals_token = @{ "!=" }
11+
greater_than_token = @{ ">" ~ !"=" }
12+
greater_than_or_equal_to_token = @{ ">=" }
13+
less_than_token = @{ "<" ~ !"=" }
14+
less_than_or_equal_to_token = @{ "<=" }
15+
and_token = @{ "and" }
16+
or_token = @{ "or" }
917

1018
// Literals
1119
true_literal = @{ "true" | "True" | "TRUE" }
@@ -40,6 +48,16 @@ identifier_literal = @{ ("_" | ASCII_ALPHA) ~ ("_" | ASCII_ALPHANUMERIC)* }
4048
real_expression = { "real(" ~ (positive_infinity_token|negative_infinity_token|double_literal|integer_literal) ~ ")" }
4149
datetime_expression = { "datetime(" ~ datetime_literal ~ ")" }
4250

51+
comparison_expression = { scalar_expression ~ (equals_token|not_equals_token|greater_than_token|greater_than_or_equal_to_token|less_than_token|less_than_or_equal_to_token) ~ scalar_expression }
52+
scalar_expression = {
53+
(real_expression | datetime_expression | boolean_expression | double_literal | integer_literal | string_literal | accessor_expression)
54+
| ("(" ~ scalar_expression ~ ")")
55+
| ("(" ~ logical_expression ~ ")")
56+
}
57+
boolean_expression = _{ true_literal | false_literal }
58+
logical_expressions = _{ comparison_expression|boolean_expression|accessor_expression }
59+
logical_expression = { (logical_expressions|("(" ~ logical_expression ~ ")")) ~ ((and_token|or_token) ~ logical_expression)* }
60+
4361
accessor_index = _{ "[" ~ (integer_literal | string_literal | (minus_token? ~ accessor_expression)) ~ "]" }
4462
accessor = _{ identifier_literal ~ accessor_index? }
4563
accessor_expression = { accessor ~ (("." ~ accessor)|accessor_index)* }

0 commit comments

Comments
 (0)