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