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, require_now_expr: bool) -> 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(require_now_expr)
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(require_now_expr)
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(require_now_expr)
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(
102 &mut self,
103 require_now_expr: bool,
104 ) -> std::result::Result<TqlParameters, TQLError> {
105 let parser = &mut self.parser;
106 let (start, end, step, lookback) = match parser.peek_token().token {
107 Token::LParen => {
108 let _consume_lparen_token = parser.next_token();
109 let start = Self::parse_string_or_number_or_word(
110 parser,
111 &[Token::Comma],
112 require_now_expr,
113 )?
114 .0;
115 let end = Self::parse_string_or_number_or_word(
116 parser,
117 &[Token::Comma],
118 require_now_expr,
119 )?
120 .0;
121
122 let (step, delimiter) = Self::parse_string_or_number_or_word(
123 parser,
124 &[Token::Comma, Token::RParen],
125 false,
126 )?;
127 let lookback = if delimiter == Token::Comma {
128 Self::parse_string_or_number_or_word(parser, &[Token::RParen], false)
129 .ok()
130 .map(|t| t.0)
131 } else {
132 None
133 };
134
135 (start, end, step, lookback)
136 }
137 _ => ("0".to_string(), "0".to_string(), "5m".to_string(), None),
138 };
139 let query = Self::parse_tql_query(parser, self.sql).context(ParserSnafu)?;
140 Ok(TqlParameters::new(start, end, step, lookback, query))
141 }
142
143 pub fn comma_or_rparen(token: &Token) -> bool {
144 Self::is_comma(token) || Self::is_rparen(token)
145 }
146
147 #[inline]
148 fn is_comma(token: &Token) -> bool {
149 matches!(token, Token::Comma)
150 }
151
152 #[inline]
153 fn is_rparen(token: &Token) -> bool {
154 matches!(token, Token::RParen)
155 }
156
157 fn has_verbose_keyword(&mut self) -> bool {
158 self.peek_token_as_string().eq_ignore_ascii_case(VERBOSE)
159 }
160
161 fn parse_format_option(&mut self) -> Option<AnalyzeFormat> {
162 if self.peek_token_as_string().eq_ignore_ascii_case(FORMAT) {
163 let _consume_format_token = self.parser.next_token();
164 if let Token::Word(w) = &self.parser.peek_token().token {
166 let format_type = w.value.to_uppercase();
167 let _consume_format_type_token = self.parser.next_token();
168 match format_type.as_str() {
169 "JSON" => Some(AnalyzeFormat::JSON),
170 "TEXT" => Some(AnalyzeFormat::TEXT),
171 "GRAPHVIZ" => Some(AnalyzeFormat::GRAPHVIZ),
172 _ => None, }
174 } else {
175 None
176 }
177 } else {
178 None
179 }
180 }
181
182 fn parse_string_or_number_or_word(
186 parser: &mut Parser,
187 delimiter_tokens: &[Token],
188 require_now_expr: bool,
189 ) -> std::result::Result<(String, Token), TQLError> {
190 let mut tokens = vec![];
191
192 while !delimiter_tokens.contains(&parser.peek_token().token) {
193 let token = parser.next_token().token;
194 if matches!(token, Token::EOF) {
195 break;
196 }
197 tokens.push(token);
198 }
199 let result = match tokens.len() {
200 0 => Err(ParserError::ParserError(
201 "Expected at least one token".to_string(),
202 ))
203 .context(ParserSnafu),
204 1 => {
205 let value = match tokens[0].clone() {
206 Token::Number(n, _) if !require_now_expr => n,
207 Token::DoubleQuotedString(s) | Token::SingleQuotedString(s)
208 if !require_now_expr =>
209 {
210 s
211 }
212 Token::Word(_) => Self::parse_tokens_to_ts(tokens, require_now_expr)?,
213 unexpected => {
214 if !require_now_expr {
215 return Err(ParserError::ParserError(format!(
216 "Expected number, string or word, but have {unexpected:?}"
217 )))
218 .context(ParserSnafu);
219 } else {
220 return Err(ParserError::ParserError(format!(
221 "Expected expression containing `now()`, but have {unexpected:?}"
222 )))
223 .context(ParserSnafu);
224 }
225 }
226 };
227 Ok(value)
228 }
229 _ => Self::parse_tokens_to_ts(tokens, require_now_expr),
230 };
231 for token in delimiter_tokens {
232 if parser.consume_token(token) {
233 return result.map(|v| (v, token.clone()));
234 }
235 }
236 Err(ParserError::ParserError(format!(
237 "Delimiters not match {delimiter_tokens:?}"
238 )))
239 .context(ParserSnafu)
240 }
241
242 fn parse_tokens_to_ts(
244 tokens: Vec<Token>,
245 require_now_expr: bool,
246 ) -> std::result::Result<String, TQLError> {
247 let parser_expr = Self::parse_to_expr(tokens)?;
248 let lit = utils::parser_expr_to_scalar_value_literal(parser_expr, require_now_expr)
249 .map_err(Box::new)
250 .context(ConvertToLogicalExpressionSnafu)?;
251
252 let second = match lit {
253 ScalarValue::TimestampNanosecond(ts_nanos, _)
254 | ScalarValue::DurationNanosecond(ts_nanos) => ts_nanos.map(|v| v / 1_000_000_000),
255 ScalarValue::TimestampMicrosecond(ts_micros, _)
256 | ScalarValue::DurationMicrosecond(ts_micros) => ts_micros.map(|v| v / 1_000_000),
257 ScalarValue::TimestampMillisecond(ts_millis, _)
258 | ScalarValue::DurationMillisecond(ts_millis) => ts_millis.map(|v| v / 1_000),
259 ScalarValue::TimestampSecond(ts_secs, _) | ScalarValue::DurationSecond(ts_secs) => {
260 ts_secs
261 }
262 _ => None,
263 };
264
265 second.map(|ts| ts.to_string()).context(EvaluationSnafu {
266 msg: format!("Failed to extract a timestamp value {lit:?}"),
267 })
268 }
269
270 fn parse_to_expr(tokens: Vec<Token>) -> std::result::Result<sqlparser::ast::Expr, TQLError> {
271 Parser::new(&GreptimeDbDialect {})
272 .with_tokens(tokens)
273 .parse_expr()
274 .context(ParserSnafu)
275 }
276
277 fn parse_tql_query(parser: &mut Parser, sql: &str) -> std::result::Result<String, ParserError> {
278 while matches!(parser.peek_token().token, Token::Comma) {
279 let _skip_token = parser.next_token();
280 }
281 let start_tql = parser.next_token();
282 if start_tql == Token::EOF {
283 return Err(ParserError::ParserError("empty TQL query".to_string()));
284 }
285
286 let start_location = start_tql.span.start;
287 let index = location_to_index(sql, &start_location);
289
290 let query = &sql[index - 1..];
291 while parser.next_token() != Token::EOF {
292 }
295 Ok(query.trim().trim_end_matches(';').to_string())
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use common_error::ext::ErrorExt;
303
304 use super::*;
305 use crate::dialect::GreptimeDbDialect;
306 use crate::parser::ParseOptions;
307
308 fn parse_into_statement(sql: &str) -> Statement {
309 let mut result =
310 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
311 .unwrap();
312 assert_eq!(1, result.len());
313 result.remove(0)
314 }
315
316 #[test]
317 fn test_require_now_expr() {
318 let sql = "TQL EVAL (now() - now(), now() - (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
319
320 let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
321 let statement = parser.parse_tql(true).unwrap();
322 match statement {
323 Statement::Tql(Tql::Eval(eval)) => {
324 assert_eq!(eval.start, "0");
325 assert_eq!(eval.end, "10");
326 assert_eq!(eval.step, "1s");
327 assert_eq!(eval.lookback, None);
328 assert_eq!(
329 eval.query,
330 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
331 );
332 }
333 _ => unreachable!(),
334 };
335
336 let sql = "TQL EVAL (0, 15, '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
337
338 let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
339 let statement = parser.parse_tql(true);
340 assert!(
341 statement.is_err()
342 && format!("{:?}", statement)
343 .contains("Expected expression containing `now()`, but have "),
344 "statement: {:?}",
345 statement
346 );
347
348 let sql = "TQL EVAL (now() - now(), now() - (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
349
350 let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
351 let statement = parser.parse_tql(false).unwrap();
352 match statement {
353 Statement::Tql(Tql::Eval(eval)) => {
354 assert_eq!(eval.start, "0");
355 assert_eq!(eval.end, "10");
356 assert_eq!(eval.step, "1s");
357 assert_eq!(eval.lookback, None);
358 assert_eq!(
359 eval.query,
360 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
361 );
362 }
363 _ => unreachable!(),
364 };
365
366 let sql = "TQL EVAL (0, 15, '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
367 let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
368 let statement = parser.parse_tql(false).unwrap();
369 match statement {
370 Statement::Tql(Tql::Eval(eval)) => {
371 assert_eq!(eval.start, "0");
372 assert_eq!(eval.end, "15");
373 assert_eq!(eval.step, "1s");
374 assert_eq!(eval.lookback, None);
375 assert_eq!(
376 eval.query,
377 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
378 );
379 }
380 _ => unreachable!(),
381 };
382 }
383
384 #[test]
385 fn test_parse_tql_eval_with_functions() {
386 let sql = "TQL EVAL (now() - now(), now() - (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
387 let statement = parse_into_statement(sql);
388 match statement {
389 Statement::Tql(Tql::Eval(eval)) => {
390 assert_eq!(eval.start, "0");
391 assert_eq!(eval.end, "10");
392 assert_eq!(eval.step, "1s");
393 assert_eq!(eval.lookback, None);
394 assert_eq!(
395 eval.query,
396 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
397 );
398 }
399 _ => unreachable!(),
400 }
401
402 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";
403 match parse_into_statement(sql) {
404 Statement::Tql(Tql::Eval(eval)) => {
405 assert_eq!(eval.start, "300");
406 assert_eq!(eval.end, "1200");
407 assert_eq!(eval.step, "1m");
408 assert_eq!(eval.lookback, None);
409 assert_eq!(
410 eval.query,
411 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
412 );
413 }
414 _ => unreachable!(),
415 }
416
417 let sql = "TQL EVAL (now(), now()-'5m', '30s') http_requests_total";
418 let result =
419 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
420 assert!(result.is_err());
421 }
422
423 #[test]
424 fn test_parse_tql_eval() {
425 let sql = "TQL EVAL (1676887657, 1676887659, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
426 match parse_into_statement(sql) {
427 Statement::Tql(Tql::Eval(eval)) => {
428 assert_eq!(eval.start, "1676887657");
429 assert_eq!(eval.end, "1676887659");
430 assert_eq!(eval.step, "1m");
431 assert_eq!(eval.lookback, None);
432 assert_eq!(
433 eval.query,
434 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
435 );
436 }
437 _ => unreachable!(),
438 }
439
440 let sql = "TQL EVAL (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
441 let statement = parse_into_statement(sql);
442
443 match &statement {
444 Statement::Tql(Tql::Eval(eval)) => {
445 assert_eq!(eval.start, "1676887657.1");
446 assert_eq!(eval.end, "1676887659.5");
447 assert_eq!(eval.step, "30.3");
448 assert_eq!(eval.lookback, None);
449 assert_eq!(
450 eval.query,
451 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
452 );
453 }
454 _ => unreachable!(),
455 }
456
457 let sql2 = "TQL EVALUATE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
458 let statement2 = parse_into_statement(sql2);
459 assert_eq!(statement, statement2);
460
461 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";
462 match parse_into_statement(sql) {
463 Statement::Tql(Tql::Eval(eval)) => {
464 assert_eq!(eval.start, "2015-07-01T20:10:30.781Z");
465 assert_eq!(eval.end, "2015-07-01T20:11:00.781Z");
466 assert_eq!(eval.step, "30s");
467 assert_eq!(eval.lookback, None);
468 assert_eq!(
469 eval.query,
470 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
471 );
472 }
473 _ => unreachable!(),
474 }
475 }
476
477 #[test]
478 fn test_parse_tql_with_lookback_values() {
479 let sql = "TQL EVAL (1676887657, 1676887659, '1m', '5m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
480 match parse_into_statement(sql) {
481 Statement::Tql(Tql::Eval(eval)) => {
482 assert_eq!(eval.start, "1676887657");
483 assert_eq!(eval.end, "1676887659");
484 assert_eq!(eval.step, "1m".to_string());
485 assert_eq!(eval.lookback, Some("5m".to_string()));
486 assert_eq!(
487 eval.query,
488 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
489 );
490 }
491 _ => unreachable!(),
492 }
493
494 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";
495 match parse_into_statement(sql) {
496 Statement::Tql(Tql::Eval(eval)) => {
497 assert_eq!(eval.start, "300");
498 assert_eq!(eval.end, "1200");
499 assert_eq!(eval.step, "1m");
500 assert_eq!(eval.lookback, Some("7m".to_string()));
501 assert_eq!(
502 eval.query,
503 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
504 );
505 }
506 _ => unreachable!(),
507 }
508
509 let sql = "TQL EXPLAIN (20, 100, 10, '3m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
510 match parse_into_statement(sql) {
511 Statement::Tql(Tql::Explain(explain)) => {
512 assert_eq!(
513 explain.query,
514 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
515 );
516 assert_eq!(explain.start, "20");
517 assert_eq!(explain.end, "100");
518 assert_eq!(explain.step, "10");
519 assert_eq!(explain.lookback, Some("3m".to_string()));
520 assert!(!explain.is_verbose);
521 }
522 _ => unreachable!(),
523 }
524
525 let sql = "TQL EXPLAIN VERBOSE (20, 100, 10, '3m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
526 match parse_into_statement(sql) {
527 Statement::Tql(Tql::Explain(explain)) => {
528 assert_eq!(
529 explain.query,
530 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
531 );
532 assert_eq!(explain.start, "20");
533 assert_eq!(explain.end, "100");
534 assert_eq!(explain.step, "10");
535 assert_eq!(explain.lookback, Some("3m".to_string()));
536 assert!(explain.is_verbose);
537 }
538 _ => unreachable!(),
539 }
540
541 let sql = "TQL ANALYZE (1676887657, 1676887659, '1m', '9m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
542 match parse_into_statement(sql) {
543 Statement::Tql(Tql::Analyze(analyze)) => {
544 assert_eq!(analyze.start, "1676887657");
545 assert_eq!(analyze.end, "1676887659");
546 assert_eq!(analyze.step, "1m");
547 assert_eq!(analyze.lookback, Some("9m".to_string()));
548 assert_eq!(
549 analyze.query,
550 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
551 );
552 assert!(!analyze.is_verbose);
553 }
554 _ => unreachable!(),
555 }
556
557 let sql = "TQL ANALYZE VERBOSE (1676887657, 1676887659, '1m', '9m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
558 match parse_into_statement(sql) {
559 Statement::Tql(Tql::Analyze(analyze)) => {
560 assert_eq!(analyze.start, "1676887657");
561 assert_eq!(analyze.end, "1676887659");
562 assert_eq!(analyze.step, "1m");
563 assert_eq!(analyze.lookback, Some("9m".to_string()));
564 assert_eq!(
565 analyze.query,
566 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
567 );
568 assert!(analyze.is_verbose);
569 }
570 _ => unreachable!(),
571 }
572 }
573
574 #[test]
575 fn test_parse_tql_explain() {
576 let sql = "TQL EXPLAIN http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
577 match parse_into_statement(sql) {
578 Statement::Tql(Tql::Explain(explain)) => {
579 assert_eq!(
580 explain.query,
581 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
582 );
583 assert_eq!(explain.start, "0");
584 assert_eq!(explain.end, "0");
585 assert_eq!(explain.step, "5m");
586 assert_eq!(explain.lookback, None);
587 assert!(!explain.is_verbose);
588 assert_eq!(explain.format, None);
589 }
590 _ => unreachable!(),
591 }
592
593 let sql = "TQL EXPLAIN VERBOSE http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
594 match parse_into_statement(sql) {
595 Statement::Tql(Tql::Explain(explain)) => {
596 assert_eq!(
597 explain.query,
598 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
599 );
600 assert_eq!(explain.start, "0");
601 assert_eq!(explain.end, "0");
602 assert_eq!(explain.step, "5m");
603 assert_eq!(explain.lookback, None);
604 assert!(explain.is_verbose);
605 assert_eq!(explain.format, None);
606 }
607 _ => unreachable!(),
608 }
609
610 let sql = "TQL EXPLAIN FORMAT JSON http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
611 match parse_into_statement(sql) {
612 Statement::Tql(Tql::Explain(explain)) => {
613 assert_eq!(
614 explain.query,
615 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
616 );
617 assert_eq!(explain.start, "0");
618 assert_eq!(explain.end, "0");
619 assert_eq!(explain.step, "5m");
620 assert_eq!(explain.lookback, None);
621 assert!(!explain.is_verbose);
622 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
623 }
624 _ => unreachable!(),
625 }
626
627 let sql = "TQL EXPLAIN VERBOSE FORMAT JSON http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
628 match parse_into_statement(sql) {
629 Statement::Tql(Tql::Explain(explain)) => {
630 assert_eq!(
631 explain.query,
632 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
633 );
634 assert_eq!(explain.start, "0");
635 assert_eq!(explain.end, "0");
636 assert_eq!(explain.step, "5m");
637 assert_eq!(explain.lookback, None);
638 assert!(explain.is_verbose);
639 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
640 }
641 _ => unreachable!(),
642 }
643
644 let sql = "TQL EXPLAIN FORMAT TEXT (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
645 match parse_into_statement(sql) {
646 Statement::Tql(Tql::Explain(explain)) => {
647 assert_eq!(
648 explain.query,
649 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
650 );
651 assert_eq!(explain.start, "20");
652 assert_eq!(explain.end, "100");
653 assert_eq!(explain.step, "10");
654 assert_eq!(explain.lookback, None);
655 assert!(!explain.is_verbose);
656 assert_eq!(explain.format, Some(AnalyzeFormat::TEXT));
657 }
658 _ => unreachable!(),
659 }
660
661 let sql = "TQL EXPLAIN (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
662 match parse_into_statement(sql) {
663 Statement::Tql(Tql::Explain(explain)) => {
664 assert_eq!(
665 explain.query,
666 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
667 );
668 assert_eq!(explain.start, "20");
669 assert_eq!(explain.end, "100");
670 assert_eq!(explain.step, "10");
671 assert_eq!(explain.lookback, None);
672 assert!(!explain.is_verbose);
673 assert_eq!(explain.format, None);
674 }
675 _ => unreachable!(),
676 }
677
678 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";
679 match parse_into_statement(sql) {
680 Statement::Tql(Tql::Explain(explain)) => {
681 assert_eq!(
682 explain.query,
683 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
684 );
685 assert_eq!(explain.start, "300");
686 assert_eq!(explain.end, "1200");
687 assert_eq!(explain.step, "10");
688 assert_eq!(explain.lookback, None);
689 assert!(!explain.is_verbose);
690 assert_eq!(explain.format, None);
691 }
692 _ => unreachable!(),
693 }
694
695 let sql = "TQL EXPLAIN VERBOSE (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
696 match parse_into_statement(sql) {
697 Statement::Tql(Tql::Explain(explain)) => {
698 assert_eq!(
699 explain.query,
700 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
701 );
702 assert_eq!(explain.start, "20");
703 assert_eq!(explain.end, "100");
704 assert_eq!(explain.step, "10");
705 assert_eq!(explain.lookback, None);
706 assert!(explain.is_verbose);
707 assert_eq!(explain.format, None);
708 }
709 _ => unreachable!(),
710 }
711
712 let sql = "TQL EXPLAIN verbose (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
713 match parse_into_statement(sql) {
714 Statement::Tql(Tql::Explain(explain)) => {
715 assert_eq!(
716 explain.query,
717 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
718 );
719 assert_eq!(explain.start, "20");
720 assert_eq!(explain.end, "100");
721 assert_eq!(explain.step, "10");
722 assert_eq!(explain.lookback, None);
723 assert!(explain.is_verbose);
724 assert_eq!(explain.format, None);
725 }
726 _ => unreachable!(),
727 }
728
729 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";
730 match parse_into_statement(sql) {
731 Statement::Tql(Tql::Explain(explain)) => {
732 assert_eq!(
733 explain.query,
734 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
735 );
736 assert_eq!(explain.start, "300");
737 assert_eq!(explain.end, "1200");
738 assert_eq!(explain.step, "10");
739 assert_eq!(explain.lookback, None);
740 assert!(explain.is_verbose);
741 assert_eq!(explain.format, None);
742 }
743 _ => unreachable!(),
744 }
745 }
746
747 #[test]
748 fn test_parse_tql_analyze() {
749 let sql = "TQL ANALYZE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
750 match parse_into_statement(sql) {
751 Statement::Tql(Tql::Analyze(analyze)) => {
752 assert_eq!(analyze.start, "1676887657.1");
753 assert_eq!(analyze.end, "1676887659.5");
754 assert_eq!(analyze.step, "30.3");
755 assert_eq!(analyze.lookback, None);
756 assert_eq!(
757 analyze.query,
758 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
759 );
760 assert!(!analyze.is_verbose);
761 assert_eq!(analyze.format, None);
762 }
763 _ => unreachable!(),
764 }
765
766 let sql = "TQL ANALYZE FORMAT JSON (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
767 match parse_into_statement(sql) {
768 Statement::Tql(Tql::Analyze(analyze)) => {
769 assert_eq!(analyze.start, "1676887657.1");
770 assert_eq!(analyze.end, "1676887659.5");
771 assert_eq!(analyze.step, "30.3");
772 assert_eq!(analyze.lookback, None);
773 assert_eq!(
774 analyze.query,
775 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
776 );
777 assert!(!analyze.is_verbose);
778 assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
779 }
780 _ => unreachable!(),
781 }
782
783 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";
784 match parse_into_statement(sql) {
785 Statement::Tql(Tql::Analyze(analyze)) => {
786 assert_eq!(analyze.start, "1676887657.1");
787 assert_eq!(analyze.end, "1676887659.5");
788 assert_eq!(analyze.step, "30.3");
789 assert_eq!(analyze.lookback, None);
790 assert_eq!(
791 analyze.query,
792 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
793 );
794 assert!(analyze.is_verbose);
795 assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
796 }
797 _ => unreachable!(),
798 }
799
800 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";
801 match parse_into_statement(sql) {
802 Statement::Tql(Tql::Analyze(analyze)) => {
803 assert_eq!(analyze.start, "300");
804 assert_eq!(analyze.end, "1200");
805 assert_eq!(analyze.step, "10");
806 assert_eq!(analyze.lookback, None);
807 assert_eq!(
808 analyze.query,
809 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
810 );
811 assert!(!analyze.is_verbose);
812 assert_eq!(analyze.format, None);
813 }
814 _ => unreachable!(),
815 }
816
817 let sql = "TQL ANALYZE VERBOSE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
818 match parse_into_statement(sql) {
819 Statement::Tql(Tql::Analyze(analyze)) => {
820 assert_eq!(analyze.start, "1676887657.1");
821 assert_eq!(analyze.end, "1676887659.5");
822 assert_eq!(analyze.step, "30.3");
823 assert_eq!(analyze.lookback, None);
824 assert_eq!(
825 analyze.query,
826 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
827 );
828 assert!(analyze.is_verbose);
829 assert_eq!(analyze.format, None);
830 }
831 _ => unreachable!(),
832 }
833
834 let sql = "TQL ANALYZE verbose (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
835 match parse_into_statement(sql) {
836 Statement::Tql(Tql::Analyze(analyze)) => {
837 assert_eq!(analyze.start, "1676887657.1");
838 assert_eq!(analyze.end, "1676887659.5");
839 assert_eq!(analyze.step, "30.3");
840 assert_eq!(analyze.lookback, None);
841 assert_eq!(
842 analyze.query,
843 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
844 );
845 assert!(analyze.is_verbose);
846 assert_eq!(analyze.format, None);
847 }
848 _ => unreachable!(),
849 }
850
851 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";
852 match parse_into_statement(sql) {
853 Statement::Tql(Tql::Analyze(analyze)) => {
854 assert_eq!(analyze.start, "300");
855 assert_eq!(analyze.end, "1200");
856 assert_eq!(analyze.step, "10");
857 assert_eq!(analyze.lookback, None);
858 assert_eq!(
859 analyze.query,
860 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
861 );
862 assert!(analyze.is_verbose);
863 assert_eq!(analyze.format, None);
864 }
865 _ => unreachable!(),
866 }
867 }
868
869 #[test]
870 fn test_parse_tql_format() {
871 let sql = "TQL EXPLAIN FORMAT JSON http_requests_total";
873 match parse_into_statement(sql) {
874 Statement::Tql(Tql::Explain(explain)) => {
875 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
876 assert!(!explain.is_verbose);
877 }
878 _ => unreachable!(),
879 }
880
881 let sql = "TQL EXPLAIN FORMAT TEXT http_requests_total";
883 match parse_into_statement(sql) {
884 Statement::Tql(Tql::Explain(explain)) => {
885 assert_eq!(explain.format, Some(AnalyzeFormat::TEXT));
886 assert!(!explain.is_verbose);
887 }
888 _ => unreachable!(),
889 }
890
891 let sql = "TQL EXPLAIN FORMAT GRAPHVIZ http_requests_total";
893 match parse_into_statement(sql) {
894 Statement::Tql(Tql::Explain(explain)) => {
895 assert_eq!(explain.format, Some(AnalyzeFormat::GRAPHVIZ));
896 assert!(!explain.is_verbose);
897 }
898 _ => unreachable!(),
899 }
900
901 let sql = "TQL ANALYZE VERBOSE FORMAT JSON (0,10,'5s') http_requests_total";
903 match parse_into_statement(sql) {
904 Statement::Tql(Tql::Analyze(analyze)) => {
905 assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
906 assert!(analyze.is_verbose);
907 }
908 _ => unreachable!(),
909 }
910
911 let sql = "TQL EXPLAIN FORMAT JSON (0,10,'5s') http_requests_total";
913 match parse_into_statement(sql) {
914 Statement::Tql(Tql::Explain(explain)) => {
915 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
916 assert_eq!(explain.start, "0");
917 assert_eq!(explain.end, "10");
918 assert_eq!(explain.step, "5s");
919 }
920 _ => unreachable!(),
921 }
922 }
923
924 #[test]
925 fn test_parse_tql_with_various_queries() {
926 match parse_into_statement("TQL EVAL (0, 30, '10s') , data + (1 < bool 2);")
928 {
929 Statement::Tql(Tql::Eval(eval)) => {
930 assert_eq!(eval.start, "0");
931 assert_eq!(eval.end, "30");
932 assert_eq!(eval.step, "10s");
933 assert_eq!(eval.lookback, None);
934 assert_eq!(eval.query, "data + (1 < bool 2)");
935 }
936 _ => unreachable!(),
937 }
938 match parse_into_statement("TQL EVAL (0, 10, '5s') '1+1';") {
940 Statement::Tql(Tql::Eval(eval)) => {
941 assert_eq!(eval.start, "0");
942 assert_eq!(eval.end, "10");
943 assert_eq!(eval.step, "5s");
944 assert_eq!(eval.lookback, None);
945 assert_eq!(eval.query, "'1+1'");
946 }
947 _ => unreachable!(),
948 }
949
950 match parse_into_statement("TQL EVAL (300, 300, '1s') 10 atan2 20;") {
952 Statement::Tql(Tql::Eval(eval)) => {
953 assert_eq!(eval.start, "300");
954 assert_eq!(eval.end, "300");
955 assert_eq!(eval.step, "1s");
956 assert_eq!(eval.lookback, None);
957 assert_eq!(eval.query, "10 atan2 20");
958 }
959 _ => unreachable!(),
960 }
961
962 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;";
964 match parse_into_statement(sql) {
965 Statement::Tql(Tql::Eval(eval)) => {
966 assert_eq!(eval.start, "0");
967 assert_eq!(eval.end, "30");
968 assert_eq!(eval.step, "10s");
969 assert_eq!(eval.lookback, None);
970 assert_eq!(
971 eval.query,
972 "(sum by(host) (irate(host_cpu_seconds_total{mode!='idle'}[1m0s])) / sum by (host)((irate(host_cpu_seconds_total[1m0s])))) * 100"
973 );
974 }
975 _ => unreachable!(),
976 }
977
978 match parse_into_statement("TQL EVAL (0, 10, '5s') {__name__=\"test\"}") {
980 Statement::Tql(Tql::Eval(eval)) => {
981 assert_eq!(eval.start, "0");
982 assert_eq!(eval.end, "10");
983 assert_eq!(eval.step, "5s");
984 assert_eq!(eval.lookback, None);
985 assert_eq!(eval.query, "{__name__=\"test\"}");
986 }
987 _ => unreachable!(),
988 }
989 }
990
991 #[test]
992 fn test_parse_tql_error() {
993 let dialect = &GreptimeDbDialect {};
994 let parse_options = ParseOptions::default();
995
996 let sql = "TQL EVAL (1676887657, 1676887659, 1m) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
998 let result =
999 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1000 assert!(
1001 result
1002 .output_msg()
1003 .contains("Failed to extract a timestamp value")
1004 );
1005
1006 let sql = "TQL EVAL (1676887657, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
1008 let result =
1009 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1010 assert!(
1011 result
1012 .output_msg()
1013 .contains("Failed to extract a timestamp value")
1014 );
1015
1016 let sql = "TQL EVAL (0, 30, '10s')";
1018 let result =
1019 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1020 assert!(result.output_msg().contains("empty TQL query"));
1021
1022 let sql = "tql eval (0, 0, '1s) t;;';";
1024 let result =
1025 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1026 assert!(result.output_msg().contains("Delimiters not match"));
1027 }
1028}