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