1use datafusion_common::ScalarValue;
16use snafu::{OptionExt, ResultExt};
17use sqlparser::ast::AnalyzeFormat;
18use sqlparser::keywords::Keyword;
19use sqlparser::parser::ParserError;
20use sqlparser::tokenizer::Token;
21
22use crate::error::{self, Result};
23use crate::parser::ParserContext;
24use crate::parsers::utils;
25use crate::statements::statement::Statement;
26use crate::statements::tql::{Tql, TqlAnalyze, TqlEval, TqlExplain, TqlParameters};
27use crate::util::location_to_index;
28
29pub const TQL: &str = "TQL";
30const EVAL: &str = "EVAL";
31const EVALUATE: &str = "EVALUATE";
32const VERBOSE: &str = "VERBOSE";
33const FORMAT: &str = "FORMAT";
34
35use sqlparser::parser::Parser;
36
37use crate::dialect::GreptimeDbDialect;
38use crate::parsers::error::{
39 ConvertToLogicalExpressionSnafu, EvaluationSnafu, ParserSnafu, TQLError,
40};
41
42impl ParserContext<'_> {
47 pub(crate) fn parse_tql(&mut self) -> Result<Statement> {
48 let _ = self.parser.next_token();
49
50 match self.parser.peek_token().token {
51 Token::Word(w) => {
52 let uppercase = w.value.to_uppercase();
53 let _consume_tql_keyword_token = self.parser.next_token();
54 match w.keyword {
55 Keyword::NoKeyword
56 if (uppercase == EVAL || uppercase == EVALUATE)
57 && w.quote_style.is_none() =>
58 {
59 self.parse_tql_params()
60 .map(|params| Statement::Tql(Tql::Eval(TqlEval::from(params))))
61 .context(error::TQLSyntaxSnafu)
62 }
63
64 Keyword::EXPLAIN => {
65 let is_verbose = self.has_verbose_keyword();
66 if is_verbose {
67 let _consume_verbose_token = self.parser.next_token();
68 }
69 let format = self.parse_format_option();
70 self.parse_tql_params()
71 .map(|mut params| {
72 params.is_verbose = is_verbose;
73 params.format = format;
74 Statement::Tql(Tql::Explain(TqlExplain::from(params)))
75 })
76 .context(error::TQLSyntaxSnafu)
77 }
78
79 Keyword::ANALYZE => {
80 let is_verbose = self.has_verbose_keyword();
81 if is_verbose {
82 let _consume_verbose_token = self.parser.next_token();
83 }
84 let format = self.parse_format_option();
85 self.parse_tql_params()
86 .map(|mut params| {
87 params.is_verbose = is_verbose;
88 params.format = format;
89 Statement::Tql(Tql::Analyze(TqlAnalyze::from(params)))
90 })
91 .context(error::TQLSyntaxSnafu)
92 }
93 _ => self.unsupported(self.peek_token_as_string()),
94 }
95 }
96 unexpected => self.unsupported(unexpected.to_string()),
97 }
98 }
99
100 fn parse_tql_params(&mut self) -> std::result::Result<TqlParameters, TQLError> {
101 let parser = &mut self.parser;
102 let (start, end, step, lookback) = match parser.peek_token().token {
103 Token::LParen => {
104 let _consume_lparen_token = parser.next_token();
105 let start = Self::parse_string_or_number_or_word(parser, &[Token::Comma])?.0;
106 let end = Self::parse_string_or_number_or_word(parser, &[Token::Comma])?.0;
107
108 let (step, delimiter) =
109 Self::parse_string_or_number_or_word(parser, &[Token::Comma, Token::RParen])?;
110 let lookback = if delimiter == Token::Comma {
111 Self::parse_string_or_number_or_word(parser, &[Token::RParen])
112 .ok()
113 .map(|t| t.0)
114 } else {
115 None
116 };
117
118 (start, end, step, lookback)
119 }
120 _ => ("0".to_string(), "0".to_string(), "5m".to_string(), None),
121 };
122 let query = Self::parse_tql_query(parser, self.sql).context(ParserSnafu)?;
123 Ok(TqlParameters::new(start, end, step, lookback, query))
124 }
125
126 pub fn comma_or_rparen(token: &Token) -> bool {
127 Self::is_comma(token) || Self::is_rparen(token)
128 }
129
130 #[inline]
131 fn is_comma(token: &Token) -> bool {
132 matches!(token, Token::Comma)
133 }
134
135 #[inline]
136 fn is_rparen(token: &Token) -> bool {
137 matches!(token, Token::RParen)
138 }
139
140 fn has_verbose_keyword(&mut self) -> bool {
141 self.peek_token_as_string().eq_ignore_ascii_case(VERBOSE)
142 }
143
144 fn parse_format_option(&mut self) -> Option<AnalyzeFormat> {
145 if self.peek_token_as_string().eq_ignore_ascii_case(FORMAT) {
146 let _consume_format_token = self.parser.next_token();
147 if let Token::Word(w) = &self.parser.peek_token().token {
149 let format_type = w.value.to_uppercase();
150 let _consume_format_type_token = self.parser.next_token();
151 match format_type.as_str() {
152 "JSON" => Some(AnalyzeFormat::JSON),
153 "TEXT" => Some(AnalyzeFormat::TEXT),
154 "GRAPHVIZ" => Some(AnalyzeFormat::GRAPHVIZ),
155 _ => None, }
157 } else {
158 None
159 }
160 } else {
161 None
162 }
163 }
164
165 fn parse_string_or_number_or_word(
169 parser: &mut Parser,
170 delimiter_tokens: &[Token],
171 ) -> std::result::Result<(String, Token), TQLError> {
172 let mut tokens = vec![];
173
174 while !delimiter_tokens.contains(&parser.peek_token().token) {
175 let token = parser.next_token().token;
176 if matches!(token, Token::EOF) {
177 break;
178 }
179 tokens.push(token);
180 }
181 let result = match tokens.len() {
182 0 => Err(ParserError::ParserError(
183 "Expected at least one token".to_string(),
184 ))
185 .context(ParserSnafu),
186 1 => {
187 let value = match tokens[0].clone() {
188 Token::Number(n, _) => n,
189 Token::DoubleQuotedString(s) | Token::SingleQuotedString(s) => s,
190 Token::Word(_) => Self::parse_tokens_to_ts(tokens)?,
191 unexpected => {
192 return Err(ParserError::ParserError(format!(
193 "Expected number, string or word, but have {unexpected:?}"
194 )))
195 .context(ParserSnafu);
196 }
197 };
198 Ok(value)
199 }
200 _ => Self::parse_tokens_to_ts(tokens),
201 };
202 for token in delimiter_tokens {
203 if parser.consume_token(token) {
204 return result.map(|v| (v, token.clone()));
205 }
206 }
207 Err(ParserError::ParserError(format!(
208 "Delimiters not match {delimiter_tokens:?}"
209 )))
210 .context(ParserSnafu)
211 }
212
213 fn parse_tokens_to_ts(tokens: Vec<Token>) -> std::result::Result<String, TQLError> {
215 let parser_expr = Self::parse_to_expr(tokens)?;
216 let lit = utils::parser_expr_to_scalar_value_literal(parser_expr)
217 .map_err(Box::new)
218 .context(ConvertToLogicalExpressionSnafu)?;
219
220 let second = match lit {
221 ScalarValue::TimestampNanosecond(ts_nanos, _)
222 | ScalarValue::DurationNanosecond(ts_nanos) => ts_nanos.map(|v| v / 1_000_000_000),
223 ScalarValue::TimestampMicrosecond(ts_micros, _)
224 | ScalarValue::DurationMicrosecond(ts_micros) => ts_micros.map(|v| v / 1_000_000),
225 ScalarValue::TimestampMillisecond(ts_millis, _)
226 | ScalarValue::DurationMillisecond(ts_millis) => ts_millis.map(|v| v / 1_000),
227 ScalarValue::TimestampSecond(ts_secs, _) | ScalarValue::DurationSecond(ts_secs) => {
228 ts_secs
229 }
230 _ => None,
231 };
232
233 second.map(|ts| ts.to_string()).context(EvaluationSnafu {
234 msg: format!("Failed to extract a timestamp value {lit:?}"),
235 })
236 }
237
238 fn parse_to_expr(tokens: Vec<Token>) -> std::result::Result<sqlparser::ast::Expr, TQLError> {
239 Parser::new(&GreptimeDbDialect {})
240 .with_tokens(tokens)
241 .parse_expr()
242 .context(ParserSnafu)
243 }
244
245 fn parse_tql_query(parser: &mut Parser, sql: &str) -> std::result::Result<String, ParserError> {
246 while matches!(parser.peek_token().token, Token::Comma) {
247 let _skip_token = parser.next_token();
248 }
249 let start_tql = parser.next_token();
250 if start_tql == Token::EOF {
251 return Err(ParserError::ParserError("empty TQL query".to_string()));
252 }
253
254 let start_location = start_tql.span.start;
255 let index = location_to_index(sql, &start_location);
257
258 let query = &sql[index - 1..];
259 while parser.next_token() != Token::EOF {
260 }
263 Ok(query.trim().trim_end_matches(';').to_string())
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use common_error::ext::ErrorExt;
271
272 use super::*;
273 use crate::dialect::GreptimeDbDialect;
274 use crate::parser::ParseOptions;
275
276 fn parse_into_statement(sql: &str) -> Statement {
277 let mut result =
278 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
279 .unwrap();
280 assert_eq!(1, result.len());
281 result.remove(0)
282 }
283
284 #[test]
285 fn test_parse_tql_eval_with_functions() {
286 let sql = "TQL EVAL (now() - now(), now() - (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
287 let statement = parse_into_statement(sql);
288 match statement {
289 Statement::Tql(Tql::Eval(eval)) => {
290 assert_eq!(eval.start, "0");
291 assert_eq!(eval.end, "10");
292 assert_eq!(eval.step, "1s");
293 assert_eq!(eval.lookback, None);
294 assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
295 }
296 _ => unreachable!(),
297 }
298
299 let sql = "TQL EVAL ('1970-01-01T00:05:00'::timestamp, '1970-01-01T00:10:00'::timestamp + '10 minutes'::interval, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
300 match parse_into_statement(sql) {
301 Statement::Tql(Tql::Eval(eval)) => {
302 assert_eq!(eval.start, "300");
303 assert_eq!(eval.end, "1200");
304 assert_eq!(eval.step, "1m");
305 assert_eq!(eval.lookback, None);
306 assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
307 }
308 _ => unreachable!(),
309 }
310
311 let sql = "TQL EVAL (now(), now()-'5m', '30s') http_requests_total";
312 let result =
313 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
314 assert!(result.is_err());
315 }
316
317 #[test]
318 fn test_parse_tql_eval() {
319 let sql = "TQL EVAL (1676887657, 1676887659, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
320 match parse_into_statement(sql) {
321 Statement::Tql(Tql::Eval(eval)) => {
322 assert_eq!(eval.start, "1676887657");
323 assert_eq!(eval.end, "1676887659");
324 assert_eq!(eval.step, "1m");
325 assert_eq!(eval.lookback, None);
326 assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
327 }
328 _ => unreachable!(),
329 }
330
331 let sql = "TQL EVAL (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
332 let statement = parse_into_statement(sql);
333
334 match &statement {
335 Statement::Tql(Tql::Eval(eval)) => {
336 assert_eq!(eval.start, "1676887657.1");
337 assert_eq!(eval.end, "1676887659.5");
338 assert_eq!(eval.step, "30.3");
339 assert_eq!(eval.lookback, None);
340 assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
341 }
342 _ => unreachable!(),
343 }
344
345 let sql2 = "TQL EVALUATE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
346 let statement2 = parse_into_statement(sql2);
347 assert_eq!(statement, statement2);
348
349 let sql = "tql eval ('2015-07-01T20:10:30.781Z', '2015-07-01T20:11:00.781Z', '30s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
350 match parse_into_statement(sql) {
351 Statement::Tql(Tql::Eval(eval)) => {
352 assert_eq!(eval.start, "2015-07-01T20:10:30.781Z");
353 assert_eq!(eval.end, "2015-07-01T20:11:00.781Z");
354 assert_eq!(eval.step, "30s");
355 assert_eq!(eval.lookback, None);
356 assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
357 }
358 _ => unreachable!(),
359 }
360 }
361
362 #[test]
363 fn test_parse_tql_with_lookback_values() {
364 let sql = "TQL EVAL (1676887657, 1676887659, '1m', '5m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
365 match parse_into_statement(sql) {
366 Statement::Tql(Tql::Eval(eval)) => {
367 assert_eq!(eval.start, "1676887657");
368 assert_eq!(eval.end, "1676887659");
369 assert_eq!(eval.step, "1m".to_string());
370 assert_eq!(eval.lookback, Some("5m".to_string()));
371 assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
372 }
373 _ => unreachable!(),
374 }
375
376 let sql = "TQL EVAL ('1970-01-01T00:05:00'::timestamp, '1970-01-01T00:10:00'::timestamp + '10 minutes'::interval, '1m', '7m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
377 match parse_into_statement(sql) {
378 Statement::Tql(Tql::Eval(eval)) => {
379 assert_eq!(eval.start, "300");
380 assert_eq!(eval.end, "1200");
381 assert_eq!(eval.step, "1m");
382 assert_eq!(eval.lookback, Some("7m".to_string()));
383 assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
384 }
385 _ => unreachable!(),
386 }
387
388 let sql = "TQL EXPLAIN (20, 100, 10, '3m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
389 match parse_into_statement(sql) {
390 Statement::Tql(Tql::Explain(explain)) => {
391 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
392 assert_eq!(explain.start, "20");
393 assert_eq!(explain.end, "100");
394 assert_eq!(explain.step, "10");
395 assert_eq!(explain.lookback, Some("3m".to_string()));
396 assert!(!explain.is_verbose);
397 }
398 _ => unreachable!(),
399 }
400
401 let sql = "TQL EXPLAIN VERBOSE (20, 100, 10, '3m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
402 match parse_into_statement(sql) {
403 Statement::Tql(Tql::Explain(explain)) => {
404 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
405 assert_eq!(explain.start, "20");
406 assert_eq!(explain.end, "100");
407 assert_eq!(explain.step, "10");
408 assert_eq!(explain.lookback, Some("3m".to_string()));
409 assert!(explain.is_verbose);
410 }
411 _ => unreachable!(),
412 }
413
414 let sql = "TQL ANALYZE (1676887657, 1676887659, '1m', '9m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
415 match parse_into_statement(sql) {
416 Statement::Tql(Tql::Analyze(analyze)) => {
417 assert_eq!(analyze.start, "1676887657");
418 assert_eq!(analyze.end, "1676887659");
419 assert_eq!(analyze.step, "1m");
420 assert_eq!(analyze.lookback, Some("9m".to_string()));
421 assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
422 assert!(!analyze.is_verbose);
423 }
424 _ => unreachable!(),
425 }
426
427 let sql = "TQL ANALYZE VERBOSE (1676887657, 1676887659, '1m', '9m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
428 match parse_into_statement(sql) {
429 Statement::Tql(Tql::Analyze(analyze)) => {
430 assert_eq!(analyze.start, "1676887657");
431 assert_eq!(analyze.end, "1676887659");
432 assert_eq!(analyze.step, "1m");
433 assert_eq!(analyze.lookback, Some("9m".to_string()));
434 assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
435 assert!(analyze.is_verbose);
436 }
437 _ => unreachable!(),
438 }
439 }
440
441 #[test]
442 fn test_parse_tql_explain() {
443 let sql = "TQL EXPLAIN http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
444 match parse_into_statement(sql) {
445 Statement::Tql(Tql::Explain(explain)) => {
446 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
447 assert_eq!(explain.start, "0");
448 assert_eq!(explain.end, "0");
449 assert_eq!(explain.step, "5m");
450 assert_eq!(explain.lookback, None);
451 assert!(!explain.is_verbose);
452 assert_eq!(explain.format, None);
453 }
454 _ => unreachable!(),
455 }
456
457 let sql = "TQL EXPLAIN VERBOSE http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
458 match parse_into_statement(sql) {
459 Statement::Tql(Tql::Explain(explain)) => {
460 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
461 assert_eq!(explain.start, "0");
462 assert_eq!(explain.end, "0");
463 assert_eq!(explain.step, "5m");
464 assert_eq!(explain.lookback, None);
465 assert!(explain.is_verbose);
466 assert_eq!(explain.format, None);
467 }
468 _ => unreachable!(),
469 }
470
471 let sql = "TQL EXPLAIN FORMAT JSON http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
472 match parse_into_statement(sql) {
473 Statement::Tql(Tql::Explain(explain)) => {
474 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
475 assert_eq!(explain.start, "0");
476 assert_eq!(explain.end, "0");
477 assert_eq!(explain.step, "5m");
478 assert_eq!(explain.lookback, None);
479 assert!(!explain.is_verbose);
480 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
481 }
482 _ => unreachable!(),
483 }
484
485 let sql = "TQL EXPLAIN VERBOSE FORMAT JSON http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
486 match parse_into_statement(sql) {
487 Statement::Tql(Tql::Explain(explain)) => {
488 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
489 assert_eq!(explain.start, "0");
490 assert_eq!(explain.end, "0");
491 assert_eq!(explain.step, "5m");
492 assert_eq!(explain.lookback, None);
493 assert!(explain.is_verbose);
494 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
495 }
496 _ => unreachable!(),
497 }
498
499 let sql = "TQL EXPLAIN FORMAT TEXT (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
500 match parse_into_statement(sql) {
501 Statement::Tql(Tql::Explain(explain)) => {
502 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
503 assert_eq!(explain.start, "20");
504 assert_eq!(explain.end, "100");
505 assert_eq!(explain.step, "10");
506 assert_eq!(explain.lookback, None);
507 assert!(!explain.is_verbose);
508 assert_eq!(explain.format, Some(AnalyzeFormat::TEXT));
509 }
510 _ => unreachable!(),
511 }
512
513 let sql = "TQL EXPLAIN (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
514 match parse_into_statement(sql) {
515 Statement::Tql(Tql::Explain(explain)) => {
516 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
517 assert_eq!(explain.start, "20");
518 assert_eq!(explain.end, "100");
519 assert_eq!(explain.step, "10");
520 assert_eq!(explain.lookback, None);
521 assert!(!explain.is_verbose);
522 assert_eq!(explain.format, None);
523 }
524 _ => unreachable!(),
525 }
526
527 let sql = "TQL EXPLAIN ('1970-01-01T00:05:00'::timestamp, '1970-01-01T00:10:00'::timestamp + '10 minutes'::interval, 10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
528 match parse_into_statement(sql) {
529 Statement::Tql(Tql::Explain(explain)) => {
530 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
531 assert_eq!(explain.start, "300");
532 assert_eq!(explain.end, "1200");
533 assert_eq!(explain.step, "10");
534 assert_eq!(explain.lookback, None);
535 assert!(!explain.is_verbose);
536 assert_eq!(explain.format, None);
537 }
538 _ => unreachable!(),
539 }
540
541 let sql = "TQL EXPLAIN VERBOSE (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
542 match parse_into_statement(sql) {
543 Statement::Tql(Tql::Explain(explain)) => {
544 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
545 assert_eq!(explain.start, "20");
546 assert_eq!(explain.end, "100");
547 assert_eq!(explain.step, "10");
548 assert_eq!(explain.lookback, None);
549 assert!(explain.is_verbose);
550 assert_eq!(explain.format, None);
551 }
552 _ => unreachable!(),
553 }
554
555 let sql = "TQL EXPLAIN verbose (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
556 match parse_into_statement(sql) {
557 Statement::Tql(Tql::Explain(explain)) => {
558 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
559 assert_eq!(explain.start, "20");
560 assert_eq!(explain.end, "100");
561 assert_eq!(explain.step, "10");
562 assert_eq!(explain.lookback, None);
563 assert!(explain.is_verbose);
564 assert_eq!(explain.format, None);
565 }
566 _ => unreachable!(),
567 }
568
569 let sql = "TQL EXPLAIN VERBOSE ('1970-01-01T00:05:00'::timestamp, '1970-01-01T00:10:00'::timestamp + '10 minutes'::interval, 10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
570 match parse_into_statement(sql) {
571 Statement::Tql(Tql::Explain(explain)) => {
572 assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
573 assert_eq!(explain.start, "300");
574 assert_eq!(explain.end, "1200");
575 assert_eq!(explain.step, "10");
576 assert_eq!(explain.lookback, None);
577 assert!(explain.is_verbose);
578 assert_eq!(explain.format, None);
579 }
580 _ => unreachable!(),
581 }
582 }
583
584 #[test]
585 fn test_parse_tql_analyze() {
586 let sql = "TQL ANALYZE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
587 match parse_into_statement(sql) {
588 Statement::Tql(Tql::Analyze(analyze)) => {
589 assert_eq!(analyze.start, "1676887657.1");
590 assert_eq!(analyze.end, "1676887659.5");
591 assert_eq!(analyze.step, "30.3");
592 assert_eq!(analyze.lookback, None);
593 assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
594 assert!(!analyze.is_verbose);
595 assert_eq!(analyze.format, None);
596 }
597 _ => unreachable!(),
598 }
599
600 let sql = "TQL ANALYZE FORMAT JSON (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
601 match parse_into_statement(sql) {
602 Statement::Tql(Tql::Analyze(analyze)) => {
603 assert_eq!(analyze.start, "1676887657.1");
604 assert_eq!(analyze.end, "1676887659.5");
605 assert_eq!(analyze.step, "30.3");
606 assert_eq!(analyze.lookback, None);
607 assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
608 assert!(!analyze.is_verbose);
609 assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
610 }
611 _ => unreachable!(),
612 }
613
614 let sql = "TQL ANALYZE VERBOSE FORMAT JSON (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
615 match parse_into_statement(sql) {
616 Statement::Tql(Tql::Analyze(analyze)) => {
617 assert_eq!(analyze.start, "1676887657.1");
618 assert_eq!(analyze.end, "1676887659.5");
619 assert_eq!(analyze.step, "30.3");
620 assert_eq!(analyze.lookback, None);
621 assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
622 assert!(analyze.is_verbose);
623 assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
624 }
625 _ => unreachable!(),
626 }
627
628 let sql = "TQL ANALYZE ('1970-01-01T00:05:00'::timestamp, '1970-01-01T00:10:00'::timestamp + '10 minutes'::interval, 10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
629 match parse_into_statement(sql) {
630 Statement::Tql(Tql::Analyze(analyze)) => {
631 assert_eq!(analyze.start, "300");
632 assert_eq!(analyze.end, "1200");
633 assert_eq!(analyze.step, "10");
634 assert_eq!(analyze.lookback, None);
635 assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
636 assert!(!analyze.is_verbose);
637 assert_eq!(analyze.format, None);
638 }
639 _ => unreachable!(),
640 }
641
642 let sql = "TQL ANALYZE VERBOSE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
643 match parse_into_statement(sql) {
644 Statement::Tql(Tql::Analyze(analyze)) => {
645 assert_eq!(analyze.start, "1676887657.1");
646 assert_eq!(analyze.end, "1676887659.5");
647 assert_eq!(analyze.step, "30.3");
648 assert_eq!(analyze.lookback, None);
649 assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
650 assert!(analyze.is_verbose);
651 assert_eq!(analyze.format, None);
652 }
653 _ => unreachable!(),
654 }
655
656 let sql = "TQL ANALYZE verbose (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
657 match parse_into_statement(sql) {
658 Statement::Tql(Tql::Analyze(analyze)) => {
659 assert_eq!(analyze.start, "1676887657.1");
660 assert_eq!(analyze.end, "1676887659.5");
661 assert_eq!(analyze.step, "30.3");
662 assert_eq!(analyze.lookback, None);
663 assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
664 assert!(analyze.is_verbose);
665 assert_eq!(analyze.format, None);
666 }
667 _ => unreachable!(),
668 }
669
670 let sql = "TQL ANALYZE VERBOSE ('1970-01-01T00:05:00'::timestamp, '1970-01-01T00:10:00'::timestamp + '10 minutes'::interval, 10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
671 match parse_into_statement(sql) {
672 Statement::Tql(Tql::Analyze(analyze)) => {
673 assert_eq!(analyze.start, "300");
674 assert_eq!(analyze.end, "1200");
675 assert_eq!(analyze.step, "10");
676 assert_eq!(analyze.lookback, None);
677 assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
678 assert!(analyze.is_verbose);
679 assert_eq!(analyze.format, None);
680 }
681 _ => unreachable!(),
682 }
683 }
684
685 #[test]
686 fn test_parse_tql_format() {
687 let sql = "TQL EXPLAIN FORMAT JSON http_requests_total";
689 match parse_into_statement(sql) {
690 Statement::Tql(Tql::Explain(explain)) => {
691 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
692 assert!(!explain.is_verbose);
693 }
694 _ => unreachable!(),
695 }
696
697 let sql = "TQL EXPLAIN FORMAT TEXT http_requests_total";
699 match parse_into_statement(sql) {
700 Statement::Tql(Tql::Explain(explain)) => {
701 assert_eq!(explain.format, Some(AnalyzeFormat::TEXT));
702 assert!(!explain.is_verbose);
703 }
704 _ => unreachable!(),
705 }
706
707 let sql = "TQL EXPLAIN FORMAT GRAPHVIZ http_requests_total";
709 match parse_into_statement(sql) {
710 Statement::Tql(Tql::Explain(explain)) => {
711 assert_eq!(explain.format, Some(AnalyzeFormat::GRAPHVIZ));
712 assert!(!explain.is_verbose);
713 }
714 _ => unreachable!(),
715 }
716
717 let sql = "TQL ANALYZE VERBOSE FORMAT JSON (0,10,'5s') http_requests_total";
719 match parse_into_statement(sql) {
720 Statement::Tql(Tql::Analyze(analyze)) => {
721 assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
722 assert!(analyze.is_verbose);
723 }
724 _ => unreachable!(),
725 }
726
727 let sql = "TQL EXPLAIN FORMAT JSON (0,10,'5s') http_requests_total";
729 match parse_into_statement(sql) {
730 Statement::Tql(Tql::Explain(explain)) => {
731 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
732 assert_eq!(explain.start, "0");
733 assert_eq!(explain.end, "10");
734 assert_eq!(explain.step, "5s");
735 }
736 _ => unreachable!(),
737 }
738 }
739
740 #[test]
741 fn test_parse_tql_with_various_queries() {
742 match parse_into_statement("TQL EVAL (0, 30, '10s') , data + (1 < bool 2);")
744 {
745 Statement::Tql(Tql::Eval(eval)) => {
746 assert_eq!(eval.start, "0");
747 assert_eq!(eval.end, "30");
748 assert_eq!(eval.step, "10s");
749 assert_eq!(eval.lookback, None);
750 assert_eq!(eval.query, "data + (1 < bool 2)");
751 }
752 _ => unreachable!(),
753 }
754 match parse_into_statement("TQL EVAL (0, 10, '5s') '1+1';") {
756 Statement::Tql(Tql::Eval(eval)) => {
757 assert_eq!(eval.start, "0");
758 assert_eq!(eval.end, "10");
759 assert_eq!(eval.step, "5s");
760 assert_eq!(eval.lookback, None);
761 assert_eq!(eval.query, "'1+1'");
762 }
763 _ => unreachable!(),
764 }
765
766 match parse_into_statement("TQL EVAL (300, 300, '1s') 10 atan2 20;") {
768 Statement::Tql(Tql::Eval(eval)) => {
769 assert_eq!(eval.start, "300");
770 assert_eq!(eval.end, "300");
771 assert_eq!(eval.step, "1s");
772 assert_eq!(eval.lookback, None);
773 assert_eq!(eval.query, "10 atan2 20");
774 }
775 _ => unreachable!(),
776 }
777
778 let sql = "TQL EVAL (0, 30, '10s') (sum by(host) (irate(host_cpu_seconds_total{mode!='idle'}[1m0s])) / sum by (host)((irate(host_cpu_seconds_total[1m0s])))) * 100;";
780 match parse_into_statement(sql) {
781 Statement::Tql(Tql::Eval(eval)) => {
782 assert_eq!(eval.start, "0");
783 assert_eq!(eval.end, "30");
784 assert_eq!(eval.step, "10s");
785 assert_eq!(eval.lookback, None);
786 assert_eq!(eval.query, "(sum by(host) (irate(host_cpu_seconds_total{mode!='idle'}[1m0s])) / sum by (host)((irate(host_cpu_seconds_total[1m0s])))) * 100");
787 }
788 _ => unreachable!(),
789 }
790
791 match parse_into_statement("TQL EVAL (0, 10, '5s') {__name__=\"test\"}") {
793 Statement::Tql(Tql::Eval(eval)) => {
794 assert_eq!(eval.start, "0");
795 assert_eq!(eval.end, "10");
796 assert_eq!(eval.step, "5s");
797 assert_eq!(eval.lookback, None);
798 assert_eq!(eval.query, "{__name__=\"test\"}");
799 }
800 _ => unreachable!(),
801 }
802 }
803
804 #[test]
805 fn test_parse_tql_error() {
806 let dialect = &GreptimeDbDialect {};
807 let parse_options = ParseOptions::default();
808
809 let sql = "TQL EVAL (1676887657, 1676887659, 1m) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
811 let result =
812 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
813 assert!(result
814 .output_msg()
815 .contains("Failed to extract a timestamp value"));
816
817 let sql = "TQL EVAL (1676887657, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
819 let result =
820 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
821 assert!(result
822 .output_msg()
823 .contains("Failed to extract a timestamp value"));
824
825 let sql = "TQL EVAL (0, 30, '10s')";
827 let result =
828 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
829 assert!(result.output_msg().contains("empty TQL query"));
830
831 let sql = "tql eval (0, 0, '1s) t;;';";
833 let result =
834 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
835 assert!(result.output_msg().contains("Delimiters not match"));
836 }
837}