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