sql/parsers/
tql_parser.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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
41/// TQL extension parser, including:
42/// - `TQL EVAL <query>`
43/// - `TQL EXPLAIN [VERBOSE] [FORMAT format] <query>`
44/// - `TQL ANALYZE [VERBOSE] [FORMAT format] <query>`
45impl 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    /// `require_now_expr` indicates whether the start&end must contain a `now()` function.
100    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                // Safety: safe to call next and unwrap, because we already check the param_count above.
123                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            // Parse format type
177            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, // Invalid format, ignore silently
185                }
186            } else {
187                None
188            }
189        } else {
190            None
191        }
192    }
193
194    /// Parse the expression to a literal string or a timestamp in seconds.
195    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    /// Parse the expression to a timestamp in seconds.
227    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    /// Parse the TQL query and optional alias from the given [Parser] and SQL string.
254    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        // translate the start location to the index in the sql string
268        let index = location_to_index(sql, &start_location);
269        assert!(index > 0);
270
271        let mut token = start_tql;
272        loop {
273            // Find AS keyword, which indicates "<promql> AS <alias"
274            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        // AS clause not found
301        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                // date_trunc('day', now() - interval '1' day) should resolve to start of yesterday
439                // date_trunc('day', now()) should resolve to start of today
440                // The exact values depend on when the test runs, but we can verify the structure
441                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        // Test with 4 parameters including lookback
454        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        // Test FORMAT JSON for EXPLAIN
935        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        // Test FORMAT TEXT for EXPLAIN
945        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        // Test FORMAT GRAPHVIZ for EXPLAIN
955        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        // Test VERBOSE FORMAT JSON for ANALYZE
965        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        // Test FORMAT before parameters
975        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        // query has whitespaces and comma
990        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        // query starts with a quote
1002        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        // query starts with number
1014        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        // query starts with a bracket
1026        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        // query starts with a curly bracket
1042        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        // Test TQL EVAL with alias
1057        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        // Test TQL EVAL with complex query and alias
1071        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        // Test TQL EVAL with lookback and alias
1088        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        // Test TQL EXPLAIN with alias
1102        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        // Test TQL EXPLAIN VERBOSE with alias
1118        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        // Test TQL ANALYZE with alias
1134        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        // Test TQL ANALYZE VERBOSE with alias and lookback
1150        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        // Test alias with underscore and numbers
1169        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        // Test complex PromQL expression with AS
1179        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        // Test query with semicolon and alias
1192        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        // Test without alias (ensure it still works)
1202        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        // Test AS without alias identifier
1218        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        // Test AS with invalid characters after alias
1223        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        // Test AS with empty promql query
1231        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        // invalid duration
1242        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        // missing end
1255        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        // empty TQL query
1267        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        // invalid token
1273        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}