1use chrono::{DateTime, Utc};
16use datafusion_common::ScalarValue;
17use snafu::{OptionExt, ResultExt};
18use sqlparser::ast::AnalyzeFormat;
19use sqlparser::keywords::Keyword;
20use sqlparser::parser::ParserError;
21use sqlparser::tokenizer::Token;
22
23use crate::error::{self, Result};
24use crate::parser::ParserContext;
25use crate::parsers::utils;
26use crate::statements::statement::Statement;
27use crate::statements::tql::{Tql, TqlAnalyze, TqlEval, TqlExplain, TqlParameters};
28use crate::util::location_to_index;
29
30pub const TQL: &str = "TQL";
31const EVAL: &str = "EVAL";
32const EVALUATE: &str = "EVALUATE";
33const VERBOSE: &str = "VERBOSE";
34const FORMAT: &str = "FORMAT";
35
36use sqlparser::parser::Parser;
37
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 #[cfg(feature = "enterprise")]
111 pub(crate) fn parse_parenthesized_tql(
112 &mut self,
113 is_lparen_consumed: bool,
114 require_now_expr: bool,
115 only_eval: bool,
116 ) -> Result<(Tql, String)> {
117 use crate::error::InvalidSqlSnafu;
118
119 if !is_lparen_consumed {
120 self.parser
121 .expect_token(&Token::LParen)
122 .context(error::SyntaxSnafu)?;
123 }
124
125 let tql_token = self.parser.peek_token();
126 let start_location = tql_token.span.start;
127 let mut paren_depth = 0usize;
128 let end_location;
129
130 loop {
131 let token_with_span = self.parser.peek_token();
132
133 if token_with_span.token == Token::EOF {
134 return Err(InvalidSqlSnafu {
135 msg: "Unexpected end of input while parsing TQL".to_string(),
136 }
137 .build());
138 }
139
140 if token_with_span.token == Token::RParen && paren_depth == 0 {
141 end_location = token_with_span.span.start;
142 self.parser.next_token();
143 break;
144 }
145
146 let consumed = self.parser.next_token();
147 match consumed.token {
148 Token::LParen => paren_depth += 1,
149 Token::RParen => paren_depth = paren_depth.saturating_sub(1),
150 _ => {}
151 }
152 }
153
154 let sql_len = self.sql.len();
155 let start_index = location_to_index(self.sql, &start_location);
156 snafu::ensure!(
157 start_index <= sql_len,
158 error::InvalidSqlSnafu {
159 msg: format!("Invalid location (index {} > len {})", start_index, sql_len),
160 }
161 );
162
163 let end_index = location_to_index(self.sql, &end_location);
164 snafu::ensure!(
165 end_index <= sql_len,
166 error::InvalidSqlSnafu {
167 msg: format!("Invalid location (index {} > len {})", end_index, sql_len),
168 }
169 );
170
171 let tql_sql = &self.sql[start_index..end_index];
172 let tql_sql = tql_sql.trim();
173 let raw_query = tql_sql.trim_end_matches(';');
174
175 let mut parser_ctx = ParserContext::new(&crate::dialect::GreptimeDbDialect {}, tql_sql)?;
176 parser_ctx.scheduled_time = self.scheduled_time;
177 let statement = parser_ctx.parse_tql(require_now_expr)?;
178
179 match statement {
180 Statement::Tql(tql) => match (only_eval, tql) {
181 (true, Tql::Eval(eval)) => Ok((Tql::Eval(eval), raw_query.to_string())),
182 (true, _) => Err(InvalidSqlSnafu {
183 msg: "Only TQL EVAL is supported in this context".to_string(),
184 }
185 .build()),
186 (false, tql) => Ok((tql, raw_query.to_string())),
187 },
188 _ => Err(InvalidSqlSnafu {
189 msg: "Expected TQL statement",
190 }
191 .build()),
192 }
193 }
194
195 fn parse_tql_params(
197 &mut self,
198 require_now_expr: bool,
199 ) -> std::result::Result<TqlParameters, TQLError> {
200 let scheduled_time = self.scheduled_time;
201 let parser = &mut self.parser;
202 let (start, end, step, lookback) = match parser.peek_token().token {
203 Token::LParen => {
204 let _consume_lparen_token = parser.next_token();
205 let exprs = parser
206 .parse_comma_separated(Parser::parse_expr)
207 .context(ParserSnafu)?;
208
209 let param_count = exprs.len();
210
211 if param_count != 3 && param_count != 4 {
212 return Err(ParserError::ParserError(
213 format!("Expected 3 or 4 expressions in TQL parameters (start, end, step, [lookback]), but found {}", param_count)
214 ))
215 .context(ParserSnafu);
216 }
217
218 let mut exprs_iter = exprs.into_iter();
219 let start = Self::parse_expr_to_literal_or_ts(
221 exprs_iter.next().unwrap(),
222 require_now_expr,
223 scheduled_time,
224 )?;
225 let end = Self::parse_expr_to_literal_or_ts(
226 exprs_iter.next().unwrap(),
227 require_now_expr,
228 scheduled_time,
229 )?;
230 let step = Self::parse_expr_to_literal_or_ts(
231 exprs_iter.next().unwrap(),
232 false,
233 scheduled_time,
234 )?;
235
236 let lookback = exprs_iter
237 .next()
238 .map(|expr| Self::parse_expr_to_literal_or_ts(expr, false, scheduled_time))
239 .transpose()?;
240
241 if !parser.consume_token(&Token::RParen) {
242 return Err(ParserError::ParserError(format!(
243 "Expected ')' after TQL parameters, but found: {}",
244 parser.peek_token()
245 )))
246 .context(ParserSnafu);
247 }
248
249 (start, end, step, lookback)
250 }
251 _ => ("0".to_string(), "0".to_string(), "5m".to_string(), None),
252 };
253
254 let (query, alias) = Self::parse_tql_query(parser, self.sql).context(ParserSnafu)?;
255 Ok(TqlParameters::new(start, end, step, lookback, query, alias))
256 }
257
258 pub fn comma_or_rparen(token: &Token) -> bool {
259 Self::is_comma(token) || Self::is_rparen(token)
260 }
261
262 #[inline]
263 fn is_comma(token: &Token) -> bool {
264 matches!(token, Token::Comma)
265 }
266
267 #[inline]
268 fn is_rparen(token: &Token) -> bool {
269 matches!(token, Token::RParen)
270 }
271
272 fn has_verbose_keyword(&mut self) -> bool {
273 self.peek_token_as_string().eq_ignore_ascii_case(VERBOSE)
274 }
275
276 fn parse_format_option(&mut self) -> Option<AnalyzeFormat> {
277 if self.peek_token_as_string().eq_ignore_ascii_case(FORMAT) {
278 let _consume_format_token = self.parser.next_token();
279 if let Token::Word(w) = &self.parser.peek_token().token {
281 let format_type = w.value.to_uppercase();
282 let _consume_format_type_token = self.parser.next_token();
283 match format_type.as_str() {
284 "JSON" => Some(AnalyzeFormat::JSON),
285 "TEXT" => Some(AnalyzeFormat::TEXT),
286 "GRAPHVIZ" => Some(AnalyzeFormat::GRAPHVIZ),
287 _ => None, }
289 } else {
290 None
291 }
292 } else {
293 None
294 }
295 }
296
297 fn parse_expr_to_literal_or_ts(
299 parser_expr: sqlparser::ast::Expr,
300 require_now_expr: bool,
301 scheduled_time: Option<DateTime<Utc>>,
302 ) -> std::result::Result<String, TQLError> {
303 match parser_expr {
304 sqlparser::ast::Expr::Value(v) => match v.value {
305 sqlparser::ast::Value::Number(s, _) if !require_now_expr => Ok(s),
306 sqlparser::ast::Value::DoubleQuotedString(s)
307 | sqlparser::ast::Value::SingleQuotedString(s)
308 if !require_now_expr =>
309 {
310 Ok(s)
311 }
312 unexpected => {
313 if !require_now_expr {
314 Err(ParserError::ParserError(format!(
315 "Expected number, string or word, but have {unexpected:?}"
316 )))
317 .context(ParserSnafu)
318 } else {
319 Err(ParserError::ParserError(format!(
320 "Expected expression containing `now()`, but have {unexpected:?}"
321 )))
322 .context(ParserSnafu)
323 }
324 }
325 },
326 _ => Self::parse_expr_to_ts(parser_expr, require_now_expr, scheduled_time),
327 }
328 }
329
330 fn parse_expr_to_ts(
332 parser_expr: sqlparser::ast::Expr,
333 require_now_expr: bool,
334 scheduled_time: Option<DateTime<Utc>>,
335 ) -> std::result::Result<String, TQLError> {
336 let lit = utils::parser_expr_to_scalar_value_literal_at(
337 parser_expr,
338 require_now_expr,
339 scheduled_time,
340 )
341 .map_err(Box::new)
342 .context(ConvertToLogicalExpressionSnafu)?;
343
344 let second = match lit {
345 ScalarValue::TimestampNanosecond(ts_nanos, _)
346 | ScalarValue::DurationNanosecond(ts_nanos) => ts_nanos.map(|v| v / 1_000_000_000),
347 ScalarValue::TimestampMicrosecond(ts_micros, _)
348 | ScalarValue::DurationMicrosecond(ts_micros) => ts_micros.map(|v| v / 1_000_000),
349 ScalarValue::TimestampMillisecond(ts_millis, _)
350 | ScalarValue::DurationMillisecond(ts_millis) => ts_millis.map(|v| v / 1_000),
351 ScalarValue::TimestampSecond(ts_secs, _) | ScalarValue::DurationSecond(ts_secs) => {
352 ts_secs
353 }
354 _ => None,
355 };
356
357 second.map(|ts| ts.to_string()).context(EvaluationSnafu {
358 msg: format!("Failed to extract a timestamp value {lit:?}"),
359 })
360 }
361
362 pub fn parse_tql_query(
364 parser: &mut Parser,
365 sql: &str,
366 ) -> std::result::Result<(String, Option<String>), ParserError> {
367 while matches!(parser.peek_token().token, Token::Comma) {
368 let _skip_token = parser.next_token();
369 }
370 let start_tql = parser.next_token();
371 if start_tql == Token::EOF {
372 return Err(ParserError::ParserError("empty TQL query".to_string()));
373 }
374
375 let start_location = start_tql.span.start;
376 let index = location_to_index(sql, &start_location);
378 assert!(index > 0);
379
380 let mut token = start_tql;
381 loop {
382 if matches!(&token.token, Token::Word(w) if w.keyword == Keyword::AS) {
384 let query_end_index = location_to_index(sql, &token.span.start);
385 let alias = parser.parse_identifier()?;
386 let promql = sql[index - 1..query_end_index]
387 .trim()
388 .trim_end_matches(';')
389 .to_string();
390 if promql.is_empty() {
391 return Err(ParserError::ParserError("Empty promql query".to_string()));
392 }
393
394 if parser.consume_token(&Token::EOF) || parser.consume_token(&Token::SemiColon) {
395 return Ok((promql, Some(alias.value)));
396 } else {
397 return Err(ParserError::ParserError(format!(
398 "Unexpected token after alias: {}",
399 parser.peek_token()
400 )));
401 }
402 }
403 token = parser.next_token();
404 if token == Token::EOF {
405 break;
406 }
407 }
408
409 let promql = sql[index - 1..].trim().trim_end_matches(';').to_string();
411 if promql.is_empty() {
412 return Err(ParserError::ParserError("Empty promql query".to_string()));
413 }
414 Ok((promql, None))
415 }
416}
417
418#[cfg(test)]
419mod tests {
420 use common_error::ext::ErrorExt;
421
422 use super::*;
423 use crate::dialect::GreptimeDbDialect;
424 use crate::parser::{ParseOptions, ParserContext};
425
426 fn parse_into_statement(sql: &str) -> Statement {
427 let mut result =
428 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
429 .unwrap();
430 assert_eq!(1, result.len());
431 result.remove(0)
432 }
433
434 fn parse_into_statement_at(sql: &str, scheduled_time: DateTime<Utc>) -> Statement {
435 let mut result = ParserContext::create_with_dialect(
436 sql,
437 &GreptimeDbDialect {},
438 ParseOptions {
439 scheduled_time: Some(scheduled_time),
440 },
441 )
442 .unwrap();
443 assert_eq!(1, result.len());
444 result.remove(0)
445 }
446
447 #[test]
448 fn test_parse_tql_eval_with_scheduled_time() {
449 let scheduled_time = DateTime::from_timestamp(1_700_000_000, 0).unwrap();
450 let sql = "TQL EVAL (now() - '10 minutes'::interval, now(), '1m') http_requests_total";
451
452 match parse_into_statement_at(sql, scheduled_time) {
453 Statement::Tql(Tql::Eval(eval)) => {
454 assert_eq!(eval.start, "1699999400");
455 assert_eq!(eval.end, "1700000000");
456 assert_eq!(eval.step, "1m");
457 assert_eq!(eval.query, "http_requests_total");
458 }
459 _ => unreachable!(),
460 }
461 }
462
463 #[test]
464 fn test_require_now_expr() {
465 let sql = "TQL EVAL (now() - now(), now() - (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
466
467 let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
468 let statement = parser.parse_tql(true).unwrap();
469 match statement {
470 Statement::Tql(Tql::Eval(eval)) => {
471 assert_eq!(eval.start, "0");
472 assert_eq!(eval.end, "10");
473 assert_eq!(eval.step, "1s");
474 assert_eq!(eval.lookback, None);
475 assert_eq!(
476 eval.query,
477 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
478 );
479 }
480 _ => unreachable!(),
481 };
482
483 let sql = "TQL EVAL (0, 15, '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
484
485 let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
486 let statement = parser.parse_tql(true);
487 assert!(
488 statement.is_err()
489 && format!("{:?}", statement)
490 .contains("Expected expression containing `now()`, but have "),
491 "statement: {:?}",
492 statement
493 );
494
495 let sql = "TQL EVAL (now() - now(), now() - (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
496
497 let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
498 let statement = parser.parse_tql(false).unwrap();
499 match statement {
500 Statement::Tql(Tql::Eval(eval)) => {
501 assert_eq!(eval.start, "0");
502 assert_eq!(eval.end, "10");
503 assert_eq!(eval.step, "1s");
504 assert_eq!(eval.lookback, None);
505 assert_eq!(
506 eval.query,
507 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
508 );
509 }
510 _ => unreachable!(),
511 };
512
513 let sql = "TQL EVAL (0, 15, '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
514 let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
515 let statement = parser.parse_tql(false).unwrap();
516 match statement {
517 Statement::Tql(Tql::Eval(eval)) => {
518 assert_eq!(eval.start, "0");
519 assert_eq!(eval.end, "15");
520 assert_eq!(eval.step, "1s");
521 assert_eq!(eval.lookback, None);
522 assert_eq!(
523 eval.query,
524 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
525 );
526 }
527 _ => unreachable!(),
528 };
529 }
530
531 #[test]
532 fn test_parse_tql_eval_with_functions() {
533 let sql = "TQL EVAL (now() - now(), now() - (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
534 let statement = parse_into_statement(sql);
535 match statement {
536 Statement::Tql(Tql::Eval(eval)) => {
537 assert_eq!(eval.start, "0");
538 assert_eq!(eval.end, "10");
539 assert_eq!(eval.step, "1s");
540 assert_eq!(eval.lookback, None);
541 assert_eq!(
542 eval.query,
543 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
544 );
545 }
546 _ => unreachable!(),
547 }
548
549 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";
550 match parse_into_statement(sql) {
551 Statement::Tql(Tql::Eval(eval)) => {
552 assert_eq!(eval.start, "300");
553 assert_eq!(eval.end, "1200");
554 assert_eq!(eval.step, "1m");
555 assert_eq!(eval.lookback, None);
556 assert_eq!(
557 eval.query,
558 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
559 );
560 }
561 _ => unreachable!(),
562 }
563
564 let sql = "TQL EVAL (now(), now()-'5m', '30s') http_requests_total";
565 let result =
566 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
567 assert!(result.is_err());
568 }
569
570 #[test]
571 fn test_parse_tql_eval_with_date_trunc() {
572 let sql = "TQL EVAL (date_trunc('day', now() - interval '1' day), date_trunc('day', now()), '1h') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
573 let statement = parse_into_statement(sql);
574 match statement {
575 Statement::Tql(Tql::Eval(eval)) => {
576 assert!(eval.start.parse::<i64>().is_ok());
580 assert!(eval.end.parse::<i64>().is_ok());
581 assert_eq!(eval.step, "1h");
582 assert_eq!(eval.lookback, None);
583 assert_eq!(
584 eval.query,
585 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
586 );
587 }
588 _ => unreachable!(),
589 }
590
591 let sql = "TQL EVAL (date_trunc('hour', now() - interval '6' hour), date_trunc('hour', now()), '30m', '5m') cpu_usage_total";
593 let statement = parse_into_statement(sql);
594 match statement {
595 Statement::Tql(Tql::Eval(eval)) => {
596 assert!(eval.start.parse::<i64>().is_ok());
597 assert!(eval.end.parse::<i64>().is_ok());
598 assert_eq!(eval.step, "30m");
599 assert_eq!(eval.lookback, Some("5m".to_string()));
600 assert_eq!(eval.query, "cpu_usage_total");
601 }
602 _ => unreachable!(),
603 }
604 }
605
606 #[test]
607 fn test_parse_tql_analyze_with_date_trunc() {
608 let sql = "TQL ANALYZE VERBOSE FORMAT JSON (date_trunc('week', now() - interval '2' week), date_trunc('week', now()), '4h', '1h') network_bytes_total";
609 let statement = parse_into_statement(sql);
610 match statement {
611 Statement::Tql(Tql::Analyze(analyze)) => {
612 assert!(analyze.start.parse::<i64>().is_ok());
613 assert!(analyze.end.parse::<i64>().is_ok());
614 assert_eq!(analyze.step, "4h");
615 assert_eq!(analyze.lookback, Some("1h".to_string()));
616 assert_eq!(analyze.query, "network_bytes_total");
617 assert!(analyze.is_verbose);
618 assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
619 }
620 _ => unreachable!(),
621 }
622 }
623
624 #[test]
625 fn test_parse_tql_eval() {
626 let sql = "TQL EVAL (1676887657, 1676887659, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
627 match parse_into_statement(sql) {
628 Statement::Tql(Tql::Eval(eval)) => {
629 assert_eq!(eval.start, "1676887657");
630 assert_eq!(eval.end, "1676887659");
631 assert_eq!(eval.step, "1m");
632 assert_eq!(eval.lookback, None);
633 assert_eq!(
634 eval.query,
635 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
636 );
637 }
638 _ => unreachable!(),
639 }
640
641 let sql = "TQL EVAL (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
642 let statement = parse_into_statement(sql);
643
644 match &statement {
645 Statement::Tql(Tql::Eval(eval)) => {
646 assert_eq!(eval.start, "1676887657.1");
647 assert_eq!(eval.end, "1676887659.5");
648 assert_eq!(eval.step, "30.3");
649 assert_eq!(eval.lookback, None);
650 assert_eq!(
651 eval.query,
652 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
653 );
654 }
655 _ => unreachable!(),
656 }
657
658 let sql2 = "TQL EVALUATE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
659 let statement2 = parse_into_statement(sql2);
660 assert_eq!(statement, statement2);
661
662 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";
663 match parse_into_statement(sql) {
664 Statement::Tql(Tql::Eval(eval)) => {
665 assert_eq!(eval.start, "2015-07-01T20:10:30.781Z");
666 assert_eq!(eval.end, "2015-07-01T20:11:00.781Z");
667 assert_eq!(eval.step, "30s");
668 assert_eq!(eval.lookback, None);
669 assert_eq!(
670 eval.query,
671 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
672 );
673 }
674 _ => unreachable!(),
675 }
676 }
677
678 #[test]
679 fn test_parse_tql_with_lookback_values() {
680 let sql = "TQL EVAL (1676887657, 1676887659, '1m', '5m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
681 match parse_into_statement(sql) {
682 Statement::Tql(Tql::Eval(eval)) => {
683 assert_eq!(eval.start, "1676887657");
684 assert_eq!(eval.end, "1676887659");
685 assert_eq!(eval.step, "1m".to_string());
686 assert_eq!(eval.lookback, Some("5m".to_string()));
687 assert_eq!(
688 eval.query,
689 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
690 );
691 }
692 _ => unreachable!(),
693 }
694
695 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";
696 match parse_into_statement(sql) {
697 Statement::Tql(Tql::Eval(eval)) => {
698 assert_eq!(eval.start, "300");
699 assert_eq!(eval.end, "1200");
700 assert_eq!(eval.step, "1m");
701 assert_eq!(eval.lookback, Some("7m".to_string()));
702 assert_eq!(
703 eval.query,
704 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
705 );
706 }
707 _ => unreachable!(),
708 }
709
710 let sql = "TQL EXPLAIN (20, 100, 10, '3m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
711 match parse_into_statement(sql) {
712 Statement::Tql(Tql::Explain(explain)) => {
713 assert_eq!(
714 explain.query,
715 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
716 );
717 assert_eq!(explain.start, "20");
718 assert_eq!(explain.end, "100");
719 assert_eq!(explain.step, "10");
720 assert_eq!(explain.lookback, Some("3m".to_string()));
721 assert!(!explain.is_verbose);
722 }
723 _ => unreachable!(),
724 }
725
726 let sql = "TQL EXPLAIN VERBOSE (20, 100, 10, '3m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
727 match parse_into_statement(sql) {
728 Statement::Tql(Tql::Explain(explain)) => {
729 assert_eq!(
730 explain.query,
731 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
732 );
733 assert_eq!(explain.start, "20");
734 assert_eq!(explain.end, "100");
735 assert_eq!(explain.step, "10");
736 assert_eq!(explain.lookback, Some("3m".to_string()));
737 assert!(explain.is_verbose);
738 }
739 _ => unreachable!(),
740 }
741
742 let sql = "TQL ANALYZE (1676887657, 1676887659, '1m', '9m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
743 match parse_into_statement(sql) {
744 Statement::Tql(Tql::Analyze(analyze)) => {
745 assert_eq!(analyze.start, "1676887657");
746 assert_eq!(analyze.end, "1676887659");
747 assert_eq!(analyze.step, "1m");
748 assert_eq!(analyze.lookback, Some("9m".to_string()));
749 assert_eq!(
750 analyze.query,
751 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
752 );
753 assert!(!analyze.is_verbose);
754 }
755 _ => unreachable!(),
756 }
757
758 let sql = "TQL ANALYZE VERBOSE (1676887657, 1676887659, '1m', '9m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
759 match parse_into_statement(sql) {
760 Statement::Tql(Tql::Analyze(analyze)) => {
761 assert_eq!(analyze.start, "1676887657");
762 assert_eq!(analyze.end, "1676887659");
763 assert_eq!(analyze.step, "1m");
764 assert_eq!(analyze.lookback, Some("9m".to_string()));
765 assert_eq!(
766 analyze.query,
767 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
768 );
769 assert!(analyze.is_verbose);
770 }
771 _ => unreachable!(),
772 }
773 }
774
775 #[test]
776 fn test_parse_tql_explain() {
777 let sql = "TQL EXPLAIN http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
778 match parse_into_statement(sql) {
779 Statement::Tql(Tql::Explain(explain)) => {
780 assert_eq!(
781 explain.query,
782 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
783 );
784 assert_eq!(explain.start, "0");
785 assert_eq!(explain.end, "0");
786 assert_eq!(explain.step, "5m");
787 assert_eq!(explain.lookback, None);
788 assert!(!explain.is_verbose);
789 assert_eq!(explain.format, None);
790 }
791 _ => unreachable!(),
792 }
793
794 let sql = "TQL EXPLAIN VERBOSE http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
795 match parse_into_statement(sql) {
796 Statement::Tql(Tql::Explain(explain)) => {
797 assert_eq!(
798 explain.query,
799 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
800 );
801 assert_eq!(explain.start, "0");
802 assert_eq!(explain.end, "0");
803 assert_eq!(explain.step, "5m");
804 assert_eq!(explain.lookback, None);
805 assert!(explain.is_verbose);
806 assert_eq!(explain.format, None);
807 }
808 _ => unreachable!(),
809 }
810
811 let sql = "TQL EXPLAIN FORMAT JSON http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
812 match parse_into_statement(sql) {
813 Statement::Tql(Tql::Explain(explain)) => {
814 assert_eq!(
815 explain.query,
816 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
817 );
818 assert_eq!(explain.start, "0");
819 assert_eq!(explain.end, "0");
820 assert_eq!(explain.step, "5m");
821 assert_eq!(explain.lookback, None);
822 assert!(!explain.is_verbose);
823 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
824 }
825 _ => unreachable!(),
826 }
827
828 let sql = "TQL EXPLAIN VERBOSE FORMAT JSON http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
829 match parse_into_statement(sql) {
830 Statement::Tql(Tql::Explain(explain)) => {
831 assert_eq!(
832 explain.query,
833 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
834 );
835 assert_eq!(explain.start, "0");
836 assert_eq!(explain.end, "0");
837 assert_eq!(explain.step, "5m");
838 assert_eq!(explain.lookback, None);
839 assert!(explain.is_verbose);
840 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
841 }
842 _ => unreachable!(),
843 }
844
845 let sql = "TQL EXPLAIN FORMAT TEXT (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
846 match parse_into_statement(sql) {
847 Statement::Tql(Tql::Explain(explain)) => {
848 assert_eq!(
849 explain.query,
850 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
851 );
852 assert_eq!(explain.start, "20");
853 assert_eq!(explain.end, "100");
854 assert_eq!(explain.step, "10");
855 assert_eq!(explain.lookback, None);
856 assert!(!explain.is_verbose);
857 assert_eq!(explain.format, Some(AnalyzeFormat::TEXT));
858 }
859 _ => unreachable!(),
860 }
861
862 let sql = "TQL EXPLAIN (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
863 match parse_into_statement(sql) {
864 Statement::Tql(Tql::Explain(explain)) => {
865 assert_eq!(
866 explain.query,
867 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
868 );
869 assert_eq!(explain.start, "20");
870 assert_eq!(explain.end, "100");
871 assert_eq!(explain.step, "10");
872 assert_eq!(explain.lookback, None);
873 assert!(!explain.is_verbose);
874 assert_eq!(explain.format, None);
875 }
876 _ => unreachable!(),
877 }
878
879 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";
880 match parse_into_statement(sql) {
881 Statement::Tql(Tql::Explain(explain)) => {
882 assert_eq!(
883 explain.query,
884 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
885 );
886 assert_eq!(explain.start, "300");
887 assert_eq!(explain.end, "1200");
888 assert_eq!(explain.step, "10");
889 assert_eq!(explain.lookback, None);
890 assert!(!explain.is_verbose);
891 assert_eq!(explain.format, None);
892 }
893 _ => unreachable!(),
894 }
895
896 let sql = "TQL EXPLAIN VERBOSE (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
897 match parse_into_statement(sql) {
898 Statement::Tql(Tql::Explain(explain)) => {
899 assert_eq!(
900 explain.query,
901 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
902 );
903 assert_eq!(explain.start, "20");
904 assert_eq!(explain.end, "100");
905 assert_eq!(explain.step, "10");
906 assert_eq!(explain.lookback, None);
907 assert!(explain.is_verbose);
908 assert_eq!(explain.format, None);
909 }
910 _ => unreachable!(),
911 }
912
913 let sql = "TQL EXPLAIN verbose (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
914 match parse_into_statement(sql) {
915 Statement::Tql(Tql::Explain(explain)) => {
916 assert_eq!(
917 explain.query,
918 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
919 );
920 assert_eq!(explain.start, "20");
921 assert_eq!(explain.end, "100");
922 assert_eq!(explain.step, "10");
923 assert_eq!(explain.lookback, None);
924 assert!(explain.is_verbose);
925 assert_eq!(explain.format, None);
926 }
927 _ => unreachable!(),
928 }
929
930 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";
931 match parse_into_statement(sql) {
932 Statement::Tql(Tql::Explain(explain)) => {
933 assert_eq!(
934 explain.query,
935 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
936 );
937 assert_eq!(explain.start, "300");
938 assert_eq!(explain.end, "1200");
939 assert_eq!(explain.step, "10");
940 assert_eq!(explain.lookback, None);
941 assert!(explain.is_verbose);
942 assert_eq!(explain.format, None);
943 }
944 _ => unreachable!(),
945 }
946 }
947
948 #[test]
949 fn test_parse_tql_analyze() {
950 let sql = "TQL ANALYZE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
951 match parse_into_statement(sql) {
952 Statement::Tql(Tql::Analyze(analyze)) => {
953 assert_eq!(analyze.start, "1676887657.1");
954 assert_eq!(analyze.end, "1676887659.5");
955 assert_eq!(analyze.step, "30.3");
956 assert_eq!(analyze.lookback, None);
957 assert_eq!(
958 analyze.query,
959 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
960 );
961 assert!(!analyze.is_verbose);
962 assert_eq!(analyze.format, None);
963 }
964 _ => unreachable!(),
965 }
966
967 let sql = "TQL ANALYZE FORMAT JSON (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
968 match parse_into_statement(sql) {
969 Statement::Tql(Tql::Analyze(analyze)) => {
970 assert_eq!(analyze.start, "1676887657.1");
971 assert_eq!(analyze.end, "1676887659.5");
972 assert_eq!(analyze.step, "30.3");
973 assert_eq!(analyze.lookback, None);
974 assert_eq!(
975 analyze.query,
976 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
977 );
978 assert!(!analyze.is_verbose);
979 assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
980 }
981 _ => unreachable!(),
982 }
983
984 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";
985 match parse_into_statement(sql) {
986 Statement::Tql(Tql::Analyze(analyze)) => {
987 assert_eq!(analyze.start, "1676887657.1");
988 assert_eq!(analyze.end, "1676887659.5");
989 assert_eq!(analyze.step, "30.3");
990 assert_eq!(analyze.lookback, None);
991 assert_eq!(
992 analyze.query,
993 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
994 );
995 assert!(analyze.is_verbose);
996 assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
997 }
998 _ => unreachable!(),
999 }
1000
1001 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";
1002 match parse_into_statement(sql) {
1003 Statement::Tql(Tql::Analyze(analyze)) => {
1004 assert_eq!(analyze.start, "300");
1005 assert_eq!(analyze.end, "1200");
1006 assert_eq!(analyze.step, "10");
1007 assert_eq!(analyze.lookback, None);
1008 assert_eq!(
1009 analyze.query,
1010 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
1011 );
1012 assert!(!analyze.is_verbose);
1013 assert_eq!(analyze.format, None);
1014 }
1015 _ => unreachable!(),
1016 }
1017
1018 let sql = "TQL ANALYZE VERBOSE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
1019 match parse_into_statement(sql) {
1020 Statement::Tql(Tql::Analyze(analyze)) => {
1021 assert_eq!(analyze.start, "1676887657.1");
1022 assert_eq!(analyze.end, "1676887659.5");
1023 assert_eq!(analyze.step, "30.3");
1024 assert_eq!(analyze.lookback, None);
1025 assert_eq!(
1026 analyze.query,
1027 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
1028 );
1029 assert!(analyze.is_verbose);
1030 assert_eq!(analyze.format, None);
1031 }
1032 _ => unreachable!(),
1033 }
1034
1035 let sql = "TQL ANALYZE verbose (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
1036 match parse_into_statement(sql) {
1037 Statement::Tql(Tql::Analyze(analyze)) => {
1038 assert_eq!(analyze.start, "1676887657.1");
1039 assert_eq!(analyze.end, "1676887659.5");
1040 assert_eq!(analyze.step, "30.3");
1041 assert_eq!(analyze.lookback, None);
1042 assert_eq!(
1043 analyze.query,
1044 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
1045 );
1046 assert!(analyze.is_verbose);
1047 assert_eq!(analyze.format, None);
1048 }
1049 _ => unreachable!(),
1050 }
1051
1052 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";
1053 match parse_into_statement(sql) {
1054 Statement::Tql(Tql::Analyze(analyze)) => {
1055 assert_eq!(analyze.start, "300");
1056 assert_eq!(analyze.end, "1200");
1057 assert_eq!(analyze.step, "10");
1058 assert_eq!(analyze.lookback, None);
1059 assert_eq!(
1060 analyze.query,
1061 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
1062 );
1063 assert!(analyze.is_verbose);
1064 assert_eq!(analyze.format, None);
1065 }
1066 _ => unreachable!(),
1067 }
1068 }
1069
1070 #[test]
1071 fn test_parse_tql_format() {
1072 let sql = "TQL EXPLAIN FORMAT JSON http_requests_total";
1074 match parse_into_statement(sql) {
1075 Statement::Tql(Tql::Explain(explain)) => {
1076 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
1077 assert!(!explain.is_verbose);
1078 }
1079 _ => unreachable!(),
1080 }
1081
1082 let sql = "TQL EXPLAIN FORMAT TEXT http_requests_total";
1084 match parse_into_statement(sql) {
1085 Statement::Tql(Tql::Explain(explain)) => {
1086 assert_eq!(explain.format, Some(AnalyzeFormat::TEXT));
1087 assert!(!explain.is_verbose);
1088 }
1089 _ => unreachable!(),
1090 }
1091
1092 let sql = "TQL EXPLAIN FORMAT GRAPHVIZ http_requests_total";
1094 match parse_into_statement(sql) {
1095 Statement::Tql(Tql::Explain(explain)) => {
1096 assert_eq!(explain.format, Some(AnalyzeFormat::GRAPHVIZ));
1097 assert!(!explain.is_verbose);
1098 }
1099 _ => unreachable!(),
1100 }
1101
1102 let sql = "TQL ANALYZE VERBOSE FORMAT JSON (0,10,'5s') http_requests_total";
1104 match parse_into_statement(sql) {
1105 Statement::Tql(Tql::Analyze(analyze)) => {
1106 assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
1107 assert!(analyze.is_verbose);
1108 }
1109 _ => unreachable!(),
1110 }
1111
1112 let sql = "TQL EXPLAIN FORMAT JSON (0,10,'5s') http_requests_total";
1114 match parse_into_statement(sql) {
1115 Statement::Tql(Tql::Explain(explain)) => {
1116 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
1117 assert_eq!(explain.start, "0");
1118 assert_eq!(explain.end, "10");
1119 assert_eq!(explain.step, "5s");
1120 }
1121 _ => unreachable!(),
1122 }
1123 }
1124
1125 #[test]
1126 fn test_parse_tql_with_various_queries() {
1127 match parse_into_statement("TQL EVAL (0, 30, '10s') , data + (1 < bool 2);")
1129 {
1130 Statement::Tql(Tql::Eval(eval)) => {
1131 assert_eq!(eval.start, "0");
1132 assert_eq!(eval.end, "30");
1133 assert_eq!(eval.step, "10s");
1134 assert_eq!(eval.lookback, None);
1135 assert_eq!(eval.query, "data + (1 < bool 2)");
1136 }
1137 _ => unreachable!(),
1138 }
1139 match parse_into_statement("TQL EVAL (0, 10, '5s') '1+1';") {
1141 Statement::Tql(Tql::Eval(eval)) => {
1142 assert_eq!(eval.start, "0");
1143 assert_eq!(eval.end, "10");
1144 assert_eq!(eval.step, "5s");
1145 assert_eq!(eval.lookback, None);
1146 assert_eq!(eval.query, "'1+1'");
1147 }
1148 _ => unreachable!(),
1149 }
1150
1151 match parse_into_statement("TQL EVAL (300, 300, '1s') 10 atan2 20;") {
1153 Statement::Tql(Tql::Eval(eval)) => {
1154 assert_eq!(eval.start, "300");
1155 assert_eq!(eval.end, "300");
1156 assert_eq!(eval.step, "1s");
1157 assert_eq!(eval.lookback, None);
1158 assert_eq!(eval.query, "10 atan2 20");
1159 }
1160 _ => unreachable!(),
1161 }
1162
1163 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;";
1165 match parse_into_statement(sql) {
1166 Statement::Tql(Tql::Eval(eval)) => {
1167 assert_eq!(eval.start, "0");
1168 assert_eq!(eval.end, "30");
1169 assert_eq!(eval.step, "10s");
1170 assert_eq!(eval.lookback, None);
1171 assert_eq!(
1172 eval.query,
1173 "(sum by(host) (irate(host_cpu_seconds_total{mode!='idle'}[1m0s])) / sum by (host)((irate(host_cpu_seconds_total[1m0s])))) * 100"
1174 );
1175 }
1176 _ => unreachable!(),
1177 }
1178
1179 match parse_into_statement("TQL EVAL (0, 10, '5s') {__name__=\"test\"}") {
1181 Statement::Tql(Tql::Eval(eval)) => {
1182 assert_eq!(eval.start, "0");
1183 assert_eq!(eval.end, "10");
1184 assert_eq!(eval.step, "5s");
1185 assert_eq!(eval.lookback, None);
1186 assert_eq!(eval.query, "{__name__=\"test\"}");
1187 }
1188 _ => unreachable!(),
1189 }
1190 }
1191
1192 #[test]
1193 fn test_parse_tql_with_alias() {
1194 let sql = "TQL EVAL (0, 30, '10s') http_requests_total AS my_metric";
1196 match parse_into_statement(sql) {
1197 Statement::Tql(Tql::Eval(eval)) => {
1198 assert_eq!(eval.start, "0");
1199 assert_eq!(eval.end, "30");
1200 assert_eq!(eval.step, "10s");
1201 assert_eq!(eval.lookback, None);
1202 assert_eq!(eval.query, "http_requests_total");
1203 assert_eq!(eval.alias, Some("my_metric".to_string()));
1204 }
1205 _ => unreachable!(),
1206 }
1207
1208 let sql = "TQL EVAL (1676887657, 1676887659, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m AS web_requests";
1210 match parse_into_statement(sql) {
1211 Statement::Tql(Tql::Eval(eval)) => {
1212 assert_eq!(eval.start, "1676887657");
1213 assert_eq!(eval.end, "1676887659");
1214 assert_eq!(eval.step, "1m");
1215 assert_eq!(eval.lookback, None);
1216 assert_eq!(
1217 eval.query,
1218 "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
1219 );
1220 assert_eq!(eval.alias, Some("web_requests".to_string()));
1221 }
1222 _ => unreachable!(),
1223 }
1224
1225 let sql = "TQL EVAL (0, 100, '30s', '5m') cpu_usage_total AS cpu_metrics";
1227 match parse_into_statement(sql) {
1228 Statement::Tql(Tql::Eval(eval)) => {
1229 assert_eq!(eval.start, "0");
1230 assert_eq!(eval.end, "100");
1231 assert_eq!(eval.step, "30s");
1232 assert_eq!(eval.lookback, Some("5m".to_string()));
1233 assert_eq!(eval.query, "cpu_usage_total");
1234 assert_eq!(eval.alias, Some("cpu_metrics".to_string()));
1235 }
1236 _ => unreachable!(),
1237 }
1238
1239 let sql = "TQL EXPLAIN (20, 100, '10s') memory_usage{app='web'} AS memory_data";
1241 match parse_into_statement(sql) {
1242 Statement::Tql(Tql::Explain(explain)) => {
1243 assert_eq!(explain.start, "20");
1244 assert_eq!(explain.end, "100");
1245 assert_eq!(explain.step, "10s");
1246 assert_eq!(explain.lookback, None);
1247 assert_eq!(explain.query, "memory_usage{app='web'}");
1248 assert_eq!(explain.alias, Some("memory_data".to_string()));
1249 assert!(!explain.is_verbose);
1250 assert_eq!(explain.format, None);
1251 }
1252 _ => unreachable!(),
1253 }
1254
1255 let sql = "TQL EXPLAIN VERBOSE FORMAT JSON (0, 50, '5s') disk_io_rate AS disk_metrics";
1257 match parse_into_statement(sql) {
1258 Statement::Tql(Tql::Explain(explain)) => {
1259 assert_eq!(explain.start, "0");
1260 assert_eq!(explain.end, "50");
1261 assert_eq!(explain.step, "5s");
1262 assert_eq!(explain.lookback, None);
1263 assert_eq!(explain.query, "disk_io_rate");
1264 assert_eq!(explain.alias, Some("disk_metrics".to_string()));
1265 assert!(explain.is_verbose);
1266 assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
1267 }
1268 _ => unreachable!(),
1269 }
1270
1271 let sql = "TQL ANALYZE (100, 200, '1m') network_bytes_total AS network_stats";
1273 match parse_into_statement(sql) {
1274 Statement::Tql(Tql::Analyze(analyze)) => {
1275 assert_eq!(analyze.start, "100");
1276 assert_eq!(analyze.end, "200");
1277 assert_eq!(analyze.step, "1m");
1278 assert_eq!(analyze.lookback, None);
1279 assert_eq!(analyze.query, "network_bytes_total");
1280 assert_eq!(analyze.alias, Some("network_stats".to_string()));
1281 assert!(!analyze.is_verbose);
1282 assert_eq!(analyze.format, None);
1283 }
1284 _ => unreachable!(),
1285 }
1286
1287 let sql = "TQL ANALYZE VERBOSE FORMAT TEXT (0, 1000, '2m', '30s') error_rate{service='api'} AS api_errors";
1289 match parse_into_statement(sql) {
1290 Statement::Tql(Tql::Analyze(analyze)) => {
1291 assert_eq!(analyze.start, "0");
1292 assert_eq!(analyze.end, "1000");
1293 assert_eq!(analyze.step, "2m");
1294 assert_eq!(analyze.lookback, Some("30s".to_string()));
1295 assert_eq!(analyze.query, "error_rate{service='api'}");
1296 assert_eq!(analyze.alias, Some("api_errors".to_string()));
1297 assert!(analyze.is_verbose);
1298 assert_eq!(analyze.format, Some(AnalyzeFormat::TEXT));
1299 }
1300 _ => unreachable!(),
1301 }
1302 }
1303
1304 #[test]
1305 fn test_parse_tql_alias_edge_cases() {
1306 let sql = "TQL EVAL (0, 10, '5s') test_metric AS metric_123";
1308 match parse_into_statement(sql) {
1309 Statement::Tql(Tql::Eval(eval)) => {
1310 assert_eq!(eval.query, "test_metric");
1311 assert_eq!(eval.alias, Some("metric_123".to_string()));
1312 }
1313 _ => unreachable!(),
1314 }
1315
1316 let sql = r#"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 AS cpu_utilization;"#;
1318 match parse_into_statement(sql) {
1319 Statement::Tql(Tql::Eval(eval)) => {
1320 assert_eq!(
1321 eval.query,
1322 "(sum by(host) (irate(host_cpu_seconds_total{mode!='idle'}[1m0s])) / sum by (host)((irate(host_cpu_seconds_total[1m0s])))) * 100"
1323 );
1324 assert_eq!(eval.alias, Some("cpu_utilization".to_string()));
1325 }
1326 _ => unreachable!(),
1327 }
1328
1329 let sql = "TQL EVAL (0, 10, '5s') simple_metric AS my_alias";
1331 match parse_into_statement(sql) {
1332 Statement::Tql(Tql::Eval(eval)) => {
1333 assert_eq!(eval.query, "simple_metric");
1334 assert_eq!(eval.alias, Some("my_alias".to_string()));
1335 }
1336 _ => unreachable!(),
1337 }
1338
1339 let sql = "TQL EVAL (0, 10, '5s') test_metric_no_alias";
1341 match parse_into_statement(sql) {
1342 Statement::Tql(Tql::Eval(eval)) => {
1343 assert_eq!(eval.query, "test_metric_no_alias");
1344 assert_eq!(eval.alias, None);
1345 }
1346 _ => unreachable!(),
1347 }
1348 }
1349
1350 #[test]
1351 fn test_parse_tql_alias_errors() {
1352 let dialect = &GreptimeDbDialect {};
1353 let parse_options = ParseOptions::default();
1354
1355 let sql = "TQL EVAL (0, 10, '5s') test_metric AS";
1357 let result = ParserContext::create_with_dialect(sql, dialect, parse_options.clone());
1358 assert!(result.is_err(), "Should fail when AS has no identifier");
1359
1360 let sql = "TQL EVAL (0, 10, '5s') test_metric AS alias extra_token";
1362 let result = ParserContext::create_with_dialect(sql, dialect, parse_options.clone());
1363 assert!(
1364 result.is_err(),
1365 "Should fail with unexpected token after alias"
1366 );
1367
1368 let sql = "TQL EVAL (0, 10, '5s') AS alias";
1370 let result = ParserContext::create_with_dialect(sql, dialect, parse_options.clone());
1371 assert!(result.is_err(), "Should fail with empty promql query");
1372 }
1373
1374 #[test]
1375 fn test_parse_tql_error() {
1376 let dialect = &GreptimeDbDialect {};
1377 let parse_options = ParseOptions::default();
1378
1379 let sql = "TQL EVAL (1676887657, 1676887659, 1m) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
1381 let result =
1382 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1383
1384 assert!(
1385 result
1386 .output_msg()
1387 .contains("Expected ')' after TQL parameters, but found: m"),
1388 "{}",
1389 result.output_msg()
1390 );
1391
1392 let sql = "TQL EVAL (1676887657, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
1394 let result =
1395 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1396 assert!(
1397 result
1398 .output_msg()
1399 .contains("Expected 3 or 4 expressions in TQL parameters"),
1400 "{}",
1401 result.output_msg()
1402 );
1403
1404 let sql = "TQL EVAL (0, 30, '10s')";
1406 let result =
1407 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1408 assert!(result.output_msg().contains("empty TQL query"));
1409
1410 let sql = "tql eval (0, 0, '1s) t;;';";
1412 let result =
1413 ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1414 assert!(
1415 result
1416 .output_msg()
1417 .contains("Expected ')' after TQL parameters, but found: ;"),
1418 "{}",
1419 result.output_msg()
1420 );
1421 }
1422
1423 #[cfg(feature = "enterprise")]
1424 #[test]
1425 fn test_parse_parenthesized_tql_only_eval_allowed() {
1426 let sql = "(TQL EXPLAIN http_requests_total)";
1427 let mut ctx = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
1428 let result = ctx.parse_parenthesized_tql(false, false, true);
1429 assert!(result.is_err());
1430 }
1431
1432 #[cfg(feature = "enterprise")]
1433 #[test]
1434 fn test_parse_parenthesized_tql_allow_non_eval_when_flag_false() {
1435 let sql = "(TQL EXPLAIN http_requests_total)";
1436 let mut ctx = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
1437
1438 let (tql, raw) = ctx
1439 .parse_parenthesized_tql(false, false, false)
1440 .expect("should allow non-eval when flag is false");
1441
1442 match tql {
1443 Tql::Explain(_) => {}
1444 _ => panic!("Expected TQL Explain variant"),
1445 }
1446 assert!(raw.contains("TQL EXPLAIN"));
1447 }
1448
1449 #[cfg(feature = "enterprise")]
1450 #[test]
1451 fn test_parse_parenthesized_tql_without_lparen() {
1452 let sql = "TQL EVAL http_requests_total)";
1453 let mut ctx = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
1454 let result = ctx.parse_parenthesized_tql(false, false, true);
1455 assert!(result.is_err());
1456 }
1457
1458 #[cfg(feature = "enterprise")]
1459 #[test]
1460 fn test_parse_parenthesized_tql_without_rparen() {
1461 let sql = "(TQL EVAL http_requests_total";
1462 let mut ctx = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
1463 let result = ctx.parse_parenthesized_tql(false, false, true);
1464 assert!(result.is_err());
1465 }
1466}