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::dialect::GreptimeDbDialect;
38use crate::parsers::error::{
39    ConvertToLogicalExpressionSnafu, EvaluationSnafu, ParserSnafu, TQLError,
40};
41
42/// TQL extension parser, including:
43/// - `TQL EVAL <query>`
44/// - `TQL EXPLAIN [VERBOSE] [FORMAT format] <query>`
45/// - `TQL ANALYZE [VERBOSE] [FORMAT format] <query>`
46impl ParserContext<'_> {
47    pub(crate) fn parse_tql(&mut self, require_now_expr: bool) -> Result<Statement> {
48        let _ = self.parser.next_token();
49
50        match self.parser.peek_token().token {
51            Token::Word(w) => {
52                let uppercase = w.value.to_uppercase();
53                let _consume_tql_keyword_token = self.parser.next_token();
54                match w.keyword {
55                    Keyword::NoKeyword
56                        if (uppercase == EVAL || uppercase == EVALUATE)
57                            && w.quote_style.is_none() =>
58                    {
59                        self.parse_tql_params(require_now_expr)
60                            .map(|params| Statement::Tql(Tql::Eval(TqlEval::from(params))))
61                            .context(error::TQLSyntaxSnafu)
62                    }
63
64                    Keyword::EXPLAIN => {
65                        let is_verbose = self.has_verbose_keyword();
66                        if is_verbose {
67                            let _consume_verbose_token = self.parser.next_token();
68                        }
69                        let format = self.parse_format_option();
70                        self.parse_tql_params(require_now_expr)
71                            .map(|mut params| {
72                                params.is_verbose = is_verbose;
73                                params.format = format;
74                                Statement::Tql(Tql::Explain(TqlExplain::from(params)))
75                            })
76                            .context(error::TQLSyntaxSnafu)
77                    }
78
79                    Keyword::ANALYZE => {
80                        let is_verbose = self.has_verbose_keyword();
81                        if is_verbose {
82                            let _consume_verbose_token = self.parser.next_token();
83                        }
84                        let format = self.parse_format_option();
85                        self.parse_tql_params(require_now_expr)
86                            .map(|mut params| {
87                                params.is_verbose = is_verbose;
88                                params.format = format;
89                                Statement::Tql(Tql::Analyze(TqlAnalyze::from(params)))
90                            })
91                            .context(error::TQLSyntaxSnafu)
92                    }
93                    _ => self.unsupported(self.peek_token_as_string()),
94                }
95            }
96            unexpected => self.unsupported(unexpected.to_string()),
97        }
98    }
99
100    /// `require_now_expr` indicates whether the start&end must contain a `now()` function.
101    fn parse_tql_params(
102        &mut self,
103        require_now_expr: bool,
104    ) -> std::result::Result<TqlParameters, TQLError> {
105        let parser = &mut self.parser;
106        let (start, end, step, lookback) = match parser.peek_token().token {
107            Token::LParen => {
108                let _consume_lparen_token = parser.next_token();
109                let start = Self::parse_string_or_number_or_word(
110                    parser,
111                    &[Token::Comma],
112                    require_now_expr,
113                )?
114                .0;
115                let end = Self::parse_string_or_number_or_word(
116                    parser,
117                    &[Token::Comma],
118                    require_now_expr,
119                )?
120                .0;
121
122                let (step, delimiter) = Self::parse_string_or_number_or_word(
123                    parser,
124                    &[Token::Comma, Token::RParen],
125                    false,
126                )?;
127                let lookback = if delimiter == Token::Comma {
128                    Self::parse_string_or_number_or_word(parser, &[Token::RParen], false)
129                        .ok()
130                        .map(|t| t.0)
131                } else {
132                    None
133                };
134
135                (start, end, step, lookback)
136            }
137            _ => ("0".to_string(), "0".to_string(), "5m".to_string(), None),
138        };
139        let query = Self::parse_tql_query(parser, self.sql).context(ParserSnafu)?;
140        Ok(TqlParameters::new(start, end, step, lookback, query))
141    }
142
143    pub fn comma_or_rparen(token: &Token) -> bool {
144        Self::is_comma(token) || Self::is_rparen(token)
145    }
146
147    #[inline]
148    fn is_comma(token: &Token) -> bool {
149        matches!(token, Token::Comma)
150    }
151
152    #[inline]
153    fn is_rparen(token: &Token) -> bool {
154        matches!(token, Token::RParen)
155    }
156
157    fn has_verbose_keyword(&mut self) -> bool {
158        self.peek_token_as_string().eq_ignore_ascii_case(VERBOSE)
159    }
160
161    fn parse_format_option(&mut self) -> Option<AnalyzeFormat> {
162        if self.peek_token_as_string().eq_ignore_ascii_case(FORMAT) {
163            let _consume_format_token = self.parser.next_token();
164            // Parse format type
165            if let Token::Word(w) = &self.parser.peek_token().token {
166                let format_type = w.value.to_uppercase();
167                let _consume_format_type_token = self.parser.next_token();
168                match format_type.as_str() {
169                    "JSON" => Some(AnalyzeFormat::JSON),
170                    "TEXT" => Some(AnalyzeFormat::TEXT),
171                    "GRAPHVIZ" => Some(AnalyzeFormat::GRAPHVIZ),
172                    _ => None, // Invalid format, ignore silently
173                }
174            } else {
175                None
176            }
177        } else {
178            None
179        }
180    }
181
182    /// Try to parse and consume a string, number or word token.
183    /// Return `Ok` if it's parsed and one of the given delimiter tokens is consumed.
184    /// The string and matched delimiter will be returned as a tuple.
185    fn parse_string_or_number_or_word(
186        parser: &mut Parser,
187        delimiter_tokens: &[Token],
188        require_now_expr: bool,
189    ) -> std::result::Result<(String, Token), TQLError> {
190        let mut tokens = vec![];
191
192        while !delimiter_tokens.contains(&parser.peek_token().token) {
193            let token = parser.next_token().token;
194            if matches!(token, Token::EOF) {
195                break;
196            }
197            tokens.push(token);
198        }
199        let result = match tokens.len() {
200            0 => Err(ParserError::ParserError(
201                "Expected at least one token".to_string(),
202            ))
203            .context(ParserSnafu),
204            1 => {
205                let value = match tokens[0].clone() {
206                    Token::Number(n, _) if !require_now_expr => n,
207                    Token::DoubleQuotedString(s) | Token::SingleQuotedString(s)
208                        if !require_now_expr =>
209                    {
210                        s
211                    }
212                    Token::Word(_) => Self::parse_tokens_to_ts(tokens, require_now_expr)?,
213                    unexpected => {
214                        if !require_now_expr {
215                            return Err(ParserError::ParserError(format!(
216                                "Expected number, string or word, but have {unexpected:?}"
217                            )))
218                            .context(ParserSnafu);
219                        } else {
220                            return Err(ParserError::ParserError(format!(
221                                "Expected expression containing `now()`, but have {unexpected:?}"
222                            )))
223                            .context(ParserSnafu);
224                        }
225                    }
226                };
227                Ok(value)
228            }
229            _ => Self::parse_tokens_to_ts(tokens, require_now_expr),
230        };
231        for token in delimiter_tokens {
232            if parser.consume_token(token) {
233                return result.map(|v| (v, token.clone()));
234            }
235        }
236        Err(ParserError::ParserError(format!(
237            "Delimiters not match {delimiter_tokens:?}"
238        )))
239        .context(ParserSnafu)
240    }
241
242    /// Parse the tokens to seconds and convert to string.
243    fn parse_tokens_to_ts(
244        tokens: Vec<Token>,
245        require_now_expr: bool,
246    ) -> std::result::Result<String, TQLError> {
247        let parser_expr = Self::parse_to_expr(tokens)?;
248        let lit = utils::parser_expr_to_scalar_value_literal(parser_expr, require_now_expr)
249            .map_err(Box::new)
250            .context(ConvertToLogicalExpressionSnafu)?;
251
252        let second = match lit {
253            ScalarValue::TimestampNanosecond(ts_nanos, _)
254            | ScalarValue::DurationNanosecond(ts_nanos) => ts_nanos.map(|v| v / 1_000_000_000),
255            ScalarValue::TimestampMicrosecond(ts_micros, _)
256            | ScalarValue::DurationMicrosecond(ts_micros) => ts_micros.map(|v| v / 1_000_000),
257            ScalarValue::TimestampMillisecond(ts_millis, _)
258            | ScalarValue::DurationMillisecond(ts_millis) => ts_millis.map(|v| v / 1_000),
259            ScalarValue::TimestampSecond(ts_secs, _) | ScalarValue::DurationSecond(ts_secs) => {
260                ts_secs
261            }
262            _ => None,
263        };
264
265        second.map(|ts| ts.to_string()).context(EvaluationSnafu {
266            msg: format!("Failed to extract a timestamp value {lit:?}"),
267        })
268    }
269
270    fn parse_to_expr(tokens: Vec<Token>) -> std::result::Result<sqlparser::ast::Expr, TQLError> {
271        Parser::new(&GreptimeDbDialect {})
272            .with_tokens(tokens)
273            .parse_expr()
274            .context(ParserSnafu)
275    }
276
277    fn parse_tql_query(parser: &mut Parser, sql: &str) -> std::result::Result<String, ParserError> {
278        while matches!(parser.peek_token().token, Token::Comma) {
279            let _skip_token = parser.next_token();
280        }
281        let start_tql = parser.next_token();
282        if start_tql == Token::EOF {
283            return Err(ParserError::ParserError("empty TQL query".to_string()));
284        }
285
286        let start_location = start_tql.span.start;
287        // translate the start location to the index in the sql string
288        let index = location_to_index(sql, &start_location);
289
290        let query = &sql[index - 1..];
291        while parser.next_token() != Token::EOF {
292            // consume all tokens
293            // TODO(dennis): supports multi TQL statements separated by ';'?
294        }
295        // remove the last ';' or tailing space if exists
296        Ok(query.trim().trim_end_matches(';').to_string())
297    }
298}
299
300#[cfg(test)]
301mod tests {
302    use common_error::ext::ErrorExt;
303
304    use super::*;
305    use crate::dialect::GreptimeDbDialect;
306    use crate::parser::ParseOptions;
307
308    fn parse_into_statement(sql: &str) -> Statement {
309        let mut result =
310            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
311                .unwrap();
312        assert_eq!(1, result.len());
313        result.remove(0)
314    }
315
316    #[test]
317    fn test_require_now_expr() {
318        let sql = "TQL EVAL (now() - now(), now() -  (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
319
320        let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
321        let statement = parser.parse_tql(true).unwrap();
322        match statement {
323            Statement::Tql(Tql::Eval(eval)) => {
324                assert_eq!(eval.start, "0");
325                assert_eq!(eval.end, "10");
326                assert_eq!(eval.step, "1s");
327                assert_eq!(eval.lookback, None);
328                assert_eq!(
329                    eval.query,
330                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
331                );
332            }
333            _ => unreachable!(),
334        };
335
336        let sql = "TQL EVAL (0, 15, '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
337
338        let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
339        let statement = parser.parse_tql(true);
340        assert!(
341            statement.is_err()
342                && format!("{:?}", statement)
343                    .contains("Expected expression containing `now()`, but have "),
344            "statement: {:?}",
345            statement
346        );
347
348        let sql = "TQL EVAL (now() - now(), now() -  (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
349
350        let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
351        let statement = parser.parse_tql(false).unwrap();
352        match statement {
353            Statement::Tql(Tql::Eval(eval)) => {
354                assert_eq!(eval.start, "0");
355                assert_eq!(eval.end, "10");
356                assert_eq!(eval.step, "1s");
357                assert_eq!(eval.lookback, None);
358                assert_eq!(
359                    eval.query,
360                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
361                );
362            }
363            _ => unreachable!(),
364        };
365
366        let sql = "TQL EVAL (0, 15, '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
367        let mut parser = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap();
368        let statement = parser.parse_tql(false).unwrap();
369        match statement {
370            Statement::Tql(Tql::Eval(eval)) => {
371                assert_eq!(eval.start, "0");
372                assert_eq!(eval.end, "15");
373                assert_eq!(eval.step, "1s");
374                assert_eq!(eval.lookback, None);
375                assert_eq!(
376                    eval.query,
377                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
378                );
379            }
380            _ => unreachable!(),
381        };
382    }
383
384    #[test]
385    fn test_parse_tql_eval_with_functions() {
386        let sql = "TQL EVAL (now() - now(), now() -  (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
387        let statement = parse_into_statement(sql);
388        match statement {
389            Statement::Tql(Tql::Eval(eval)) => {
390                assert_eq!(eval.start, "0");
391                assert_eq!(eval.end, "10");
392                assert_eq!(eval.step, "1s");
393                assert_eq!(eval.lookback, None);
394                assert_eq!(
395                    eval.query,
396                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
397                );
398            }
399            _ => unreachable!(),
400        }
401
402        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";
403        match parse_into_statement(sql) {
404            Statement::Tql(Tql::Eval(eval)) => {
405                assert_eq!(eval.start, "300");
406                assert_eq!(eval.end, "1200");
407                assert_eq!(eval.step, "1m");
408                assert_eq!(eval.lookback, None);
409                assert_eq!(
410                    eval.query,
411                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
412                );
413            }
414            _ => unreachable!(),
415        }
416
417        let sql = "TQL EVAL (now(), now()-'5m', '30s') http_requests_total";
418        let result =
419            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
420        assert!(result.is_err());
421    }
422
423    #[test]
424    fn test_parse_tql_eval() {
425        let sql = "TQL EVAL (1676887657, 1676887659, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
426        match parse_into_statement(sql) {
427            Statement::Tql(Tql::Eval(eval)) => {
428                assert_eq!(eval.start, "1676887657");
429                assert_eq!(eval.end, "1676887659");
430                assert_eq!(eval.step, "1m");
431                assert_eq!(eval.lookback, None);
432                assert_eq!(
433                    eval.query,
434                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
435                );
436            }
437            _ => unreachable!(),
438        }
439
440        let sql = "TQL EVAL (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
441        let statement = parse_into_statement(sql);
442
443        match &statement {
444            Statement::Tql(Tql::Eval(eval)) => {
445                assert_eq!(eval.start, "1676887657.1");
446                assert_eq!(eval.end, "1676887659.5");
447                assert_eq!(eval.step, "30.3");
448                assert_eq!(eval.lookback, None);
449                assert_eq!(
450                    eval.query,
451                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
452                );
453            }
454            _ => unreachable!(),
455        }
456
457        let sql2 = "TQL EVALUATE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
458        let statement2 = parse_into_statement(sql2);
459        assert_eq!(statement, statement2);
460
461        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";
462        match parse_into_statement(sql) {
463            Statement::Tql(Tql::Eval(eval)) => {
464                assert_eq!(eval.start, "2015-07-01T20:10:30.781Z");
465                assert_eq!(eval.end, "2015-07-01T20:11:00.781Z");
466                assert_eq!(eval.step, "30s");
467                assert_eq!(eval.lookback, None);
468                assert_eq!(
469                    eval.query,
470                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
471                );
472            }
473            _ => unreachable!(),
474        }
475    }
476
477    #[test]
478    fn test_parse_tql_with_lookback_values() {
479        let sql = "TQL EVAL (1676887657, 1676887659, '1m', '5m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
480        match parse_into_statement(sql) {
481            Statement::Tql(Tql::Eval(eval)) => {
482                assert_eq!(eval.start, "1676887657");
483                assert_eq!(eval.end, "1676887659");
484                assert_eq!(eval.step, "1m".to_string());
485                assert_eq!(eval.lookback, Some("5m".to_string()));
486                assert_eq!(
487                    eval.query,
488                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
489                );
490            }
491            _ => unreachable!(),
492        }
493
494        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";
495        match parse_into_statement(sql) {
496            Statement::Tql(Tql::Eval(eval)) => {
497                assert_eq!(eval.start, "300");
498                assert_eq!(eval.end, "1200");
499                assert_eq!(eval.step, "1m");
500                assert_eq!(eval.lookback, Some("7m".to_string()));
501                assert_eq!(
502                    eval.query,
503                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
504                );
505            }
506            _ => unreachable!(),
507        }
508
509        let sql = "TQL EXPLAIN (20, 100, 10, '3m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
510        match parse_into_statement(sql) {
511            Statement::Tql(Tql::Explain(explain)) => {
512                assert_eq!(
513                    explain.query,
514                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
515                );
516                assert_eq!(explain.start, "20");
517                assert_eq!(explain.end, "100");
518                assert_eq!(explain.step, "10");
519                assert_eq!(explain.lookback, Some("3m".to_string()));
520                assert!(!explain.is_verbose);
521            }
522            _ => unreachable!(),
523        }
524
525        let sql = "TQL EXPLAIN VERBOSE (20, 100, 10, '3m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
526        match parse_into_statement(sql) {
527            Statement::Tql(Tql::Explain(explain)) => {
528                assert_eq!(
529                    explain.query,
530                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
531                );
532                assert_eq!(explain.start, "20");
533                assert_eq!(explain.end, "100");
534                assert_eq!(explain.step, "10");
535                assert_eq!(explain.lookback, Some("3m".to_string()));
536                assert!(explain.is_verbose);
537            }
538            _ => unreachable!(),
539        }
540
541        let sql = "TQL ANALYZE (1676887657, 1676887659, '1m', '9m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
542        match parse_into_statement(sql) {
543            Statement::Tql(Tql::Analyze(analyze)) => {
544                assert_eq!(analyze.start, "1676887657");
545                assert_eq!(analyze.end, "1676887659");
546                assert_eq!(analyze.step, "1m");
547                assert_eq!(analyze.lookback, Some("9m".to_string()));
548                assert_eq!(
549                    analyze.query,
550                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
551                );
552                assert!(!analyze.is_verbose);
553            }
554            _ => unreachable!(),
555        }
556
557        let sql = "TQL ANALYZE VERBOSE (1676887657, 1676887659, '1m', '9m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
558        match parse_into_statement(sql) {
559            Statement::Tql(Tql::Analyze(analyze)) => {
560                assert_eq!(analyze.start, "1676887657");
561                assert_eq!(analyze.end, "1676887659");
562                assert_eq!(analyze.step, "1m");
563                assert_eq!(analyze.lookback, Some("9m".to_string()));
564                assert_eq!(
565                    analyze.query,
566                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
567                );
568                assert!(analyze.is_verbose);
569            }
570            _ => unreachable!(),
571        }
572    }
573
574    #[test]
575    fn test_parse_tql_explain() {
576        let sql = "TQL EXPLAIN http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
577        match parse_into_statement(sql) {
578            Statement::Tql(Tql::Explain(explain)) => {
579                assert_eq!(
580                    explain.query,
581                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
582                );
583                assert_eq!(explain.start, "0");
584                assert_eq!(explain.end, "0");
585                assert_eq!(explain.step, "5m");
586                assert_eq!(explain.lookback, None);
587                assert!(!explain.is_verbose);
588                assert_eq!(explain.format, None);
589            }
590            _ => unreachable!(),
591        }
592
593        let sql = "TQL EXPLAIN VERBOSE http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
594        match parse_into_statement(sql) {
595            Statement::Tql(Tql::Explain(explain)) => {
596                assert_eq!(
597                    explain.query,
598                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
599                );
600                assert_eq!(explain.start, "0");
601                assert_eq!(explain.end, "0");
602                assert_eq!(explain.step, "5m");
603                assert_eq!(explain.lookback, None);
604                assert!(explain.is_verbose);
605                assert_eq!(explain.format, None);
606            }
607            _ => unreachable!(),
608        }
609
610        let sql = "TQL EXPLAIN FORMAT JSON http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
611        match parse_into_statement(sql) {
612            Statement::Tql(Tql::Explain(explain)) => {
613                assert_eq!(
614                    explain.query,
615                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
616                );
617                assert_eq!(explain.start, "0");
618                assert_eq!(explain.end, "0");
619                assert_eq!(explain.step, "5m");
620                assert_eq!(explain.lookback, None);
621                assert!(!explain.is_verbose);
622                assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
623            }
624            _ => unreachable!(),
625        }
626
627        let sql = "TQL EXPLAIN VERBOSE FORMAT JSON http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
628        match parse_into_statement(sql) {
629            Statement::Tql(Tql::Explain(explain)) => {
630                assert_eq!(
631                    explain.query,
632                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
633                );
634                assert_eq!(explain.start, "0");
635                assert_eq!(explain.end, "0");
636                assert_eq!(explain.step, "5m");
637                assert_eq!(explain.lookback, None);
638                assert!(explain.is_verbose);
639                assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
640            }
641            _ => unreachable!(),
642        }
643
644        let sql = "TQL EXPLAIN FORMAT TEXT (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
645        match parse_into_statement(sql) {
646            Statement::Tql(Tql::Explain(explain)) => {
647                assert_eq!(
648                    explain.query,
649                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
650                );
651                assert_eq!(explain.start, "20");
652                assert_eq!(explain.end, "100");
653                assert_eq!(explain.step, "10");
654                assert_eq!(explain.lookback, None);
655                assert!(!explain.is_verbose);
656                assert_eq!(explain.format, Some(AnalyzeFormat::TEXT));
657            }
658            _ => unreachable!(),
659        }
660
661        let sql = "TQL EXPLAIN (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
662        match parse_into_statement(sql) {
663            Statement::Tql(Tql::Explain(explain)) => {
664                assert_eq!(
665                    explain.query,
666                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
667                );
668                assert_eq!(explain.start, "20");
669                assert_eq!(explain.end, "100");
670                assert_eq!(explain.step, "10");
671                assert_eq!(explain.lookback, None);
672                assert!(!explain.is_verbose);
673                assert_eq!(explain.format, None);
674            }
675            _ => unreachable!(),
676        }
677
678        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";
679        match parse_into_statement(sql) {
680            Statement::Tql(Tql::Explain(explain)) => {
681                assert_eq!(
682                    explain.query,
683                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
684                );
685                assert_eq!(explain.start, "300");
686                assert_eq!(explain.end, "1200");
687                assert_eq!(explain.step, "10");
688                assert_eq!(explain.lookback, None);
689                assert!(!explain.is_verbose);
690                assert_eq!(explain.format, None);
691            }
692            _ => unreachable!(),
693        }
694
695        let sql = "TQL EXPLAIN VERBOSE (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
696        match parse_into_statement(sql) {
697            Statement::Tql(Tql::Explain(explain)) => {
698                assert_eq!(
699                    explain.query,
700                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
701                );
702                assert_eq!(explain.start, "20");
703                assert_eq!(explain.end, "100");
704                assert_eq!(explain.step, "10");
705                assert_eq!(explain.lookback, None);
706                assert!(explain.is_verbose);
707                assert_eq!(explain.format, None);
708            }
709            _ => unreachable!(),
710        }
711
712        let sql = "TQL EXPLAIN verbose (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
713        match parse_into_statement(sql) {
714            Statement::Tql(Tql::Explain(explain)) => {
715                assert_eq!(
716                    explain.query,
717                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
718                );
719                assert_eq!(explain.start, "20");
720                assert_eq!(explain.end, "100");
721                assert_eq!(explain.step, "10");
722                assert_eq!(explain.lookback, None);
723                assert!(explain.is_verbose);
724                assert_eq!(explain.format, None);
725            }
726            _ => unreachable!(),
727        }
728
729        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";
730        match parse_into_statement(sql) {
731            Statement::Tql(Tql::Explain(explain)) => {
732                assert_eq!(
733                    explain.query,
734                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
735                );
736                assert_eq!(explain.start, "300");
737                assert_eq!(explain.end, "1200");
738                assert_eq!(explain.step, "10");
739                assert_eq!(explain.lookback, None);
740                assert!(explain.is_verbose);
741                assert_eq!(explain.format, None);
742            }
743            _ => unreachable!(),
744        }
745    }
746
747    #[test]
748    fn test_parse_tql_analyze() {
749        let sql = "TQL ANALYZE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
750        match parse_into_statement(sql) {
751            Statement::Tql(Tql::Analyze(analyze)) => {
752                assert_eq!(analyze.start, "1676887657.1");
753                assert_eq!(analyze.end, "1676887659.5");
754                assert_eq!(analyze.step, "30.3");
755                assert_eq!(analyze.lookback, None);
756                assert_eq!(
757                    analyze.query,
758                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
759                );
760                assert!(!analyze.is_verbose);
761                assert_eq!(analyze.format, None);
762            }
763            _ => unreachable!(),
764        }
765
766        let sql = "TQL ANALYZE FORMAT JSON (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
767        match parse_into_statement(sql) {
768            Statement::Tql(Tql::Analyze(analyze)) => {
769                assert_eq!(analyze.start, "1676887657.1");
770                assert_eq!(analyze.end, "1676887659.5");
771                assert_eq!(analyze.step, "30.3");
772                assert_eq!(analyze.lookback, None);
773                assert_eq!(
774                    analyze.query,
775                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
776                );
777                assert!(!analyze.is_verbose);
778                assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
779            }
780            _ => unreachable!(),
781        }
782
783        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";
784        match parse_into_statement(sql) {
785            Statement::Tql(Tql::Analyze(analyze)) => {
786                assert_eq!(analyze.start, "1676887657.1");
787                assert_eq!(analyze.end, "1676887659.5");
788                assert_eq!(analyze.step, "30.3");
789                assert_eq!(analyze.lookback, None);
790                assert_eq!(
791                    analyze.query,
792                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
793                );
794                assert!(analyze.is_verbose);
795                assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
796            }
797            _ => unreachable!(),
798        }
799
800        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";
801        match parse_into_statement(sql) {
802            Statement::Tql(Tql::Analyze(analyze)) => {
803                assert_eq!(analyze.start, "300");
804                assert_eq!(analyze.end, "1200");
805                assert_eq!(analyze.step, "10");
806                assert_eq!(analyze.lookback, None);
807                assert_eq!(
808                    analyze.query,
809                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
810                );
811                assert!(!analyze.is_verbose);
812                assert_eq!(analyze.format, None);
813            }
814            _ => unreachable!(),
815        }
816
817        let sql = "TQL ANALYZE VERBOSE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
818        match parse_into_statement(sql) {
819            Statement::Tql(Tql::Analyze(analyze)) => {
820                assert_eq!(analyze.start, "1676887657.1");
821                assert_eq!(analyze.end, "1676887659.5");
822                assert_eq!(analyze.step, "30.3");
823                assert_eq!(analyze.lookback, None);
824                assert_eq!(
825                    analyze.query,
826                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
827                );
828                assert!(analyze.is_verbose);
829                assert_eq!(analyze.format, None);
830            }
831            _ => unreachable!(),
832        }
833
834        let sql = "TQL ANALYZE verbose (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
835        match parse_into_statement(sql) {
836            Statement::Tql(Tql::Analyze(analyze)) => {
837                assert_eq!(analyze.start, "1676887657.1");
838                assert_eq!(analyze.end, "1676887659.5");
839                assert_eq!(analyze.step, "30.3");
840                assert_eq!(analyze.lookback, None);
841                assert_eq!(
842                    analyze.query,
843                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
844                );
845                assert!(analyze.is_verbose);
846                assert_eq!(analyze.format, None);
847            }
848            _ => unreachable!(),
849        }
850
851        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";
852        match parse_into_statement(sql) {
853            Statement::Tql(Tql::Analyze(analyze)) => {
854                assert_eq!(analyze.start, "300");
855                assert_eq!(analyze.end, "1200");
856                assert_eq!(analyze.step, "10");
857                assert_eq!(analyze.lookback, None);
858                assert_eq!(
859                    analyze.query,
860                    "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m"
861                );
862                assert!(analyze.is_verbose);
863                assert_eq!(analyze.format, None);
864            }
865            _ => unreachable!(),
866        }
867    }
868
869    #[test]
870    fn test_parse_tql_format() {
871        // Test FORMAT JSON for EXPLAIN
872        let sql = "TQL EXPLAIN FORMAT JSON http_requests_total";
873        match parse_into_statement(sql) {
874            Statement::Tql(Tql::Explain(explain)) => {
875                assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
876                assert!(!explain.is_verbose);
877            }
878            _ => unreachable!(),
879        }
880
881        // Test FORMAT TEXT for EXPLAIN
882        let sql = "TQL EXPLAIN FORMAT TEXT http_requests_total";
883        match parse_into_statement(sql) {
884            Statement::Tql(Tql::Explain(explain)) => {
885                assert_eq!(explain.format, Some(AnalyzeFormat::TEXT));
886                assert!(!explain.is_verbose);
887            }
888            _ => unreachable!(),
889        }
890
891        // Test FORMAT GRAPHVIZ for EXPLAIN
892        let sql = "TQL EXPLAIN FORMAT GRAPHVIZ http_requests_total";
893        match parse_into_statement(sql) {
894            Statement::Tql(Tql::Explain(explain)) => {
895                assert_eq!(explain.format, Some(AnalyzeFormat::GRAPHVIZ));
896                assert!(!explain.is_verbose);
897            }
898            _ => unreachable!(),
899        }
900
901        // Test VERBOSE FORMAT JSON for ANALYZE
902        let sql = "TQL ANALYZE VERBOSE FORMAT JSON (0,10,'5s') http_requests_total";
903        match parse_into_statement(sql) {
904            Statement::Tql(Tql::Analyze(analyze)) => {
905                assert_eq!(analyze.format, Some(AnalyzeFormat::JSON));
906                assert!(analyze.is_verbose);
907            }
908            _ => unreachable!(),
909        }
910
911        // Test FORMAT before parameters
912        let sql = "TQL EXPLAIN FORMAT JSON (0,10,'5s') http_requests_total";
913        match parse_into_statement(sql) {
914            Statement::Tql(Tql::Explain(explain)) => {
915                assert_eq!(explain.format, Some(AnalyzeFormat::JSON));
916                assert_eq!(explain.start, "0");
917                assert_eq!(explain.end, "10");
918                assert_eq!(explain.step, "5s");
919            }
920            _ => unreachable!(),
921        }
922    }
923
924    #[test]
925    fn test_parse_tql_with_various_queries() {
926        // query has whitespaces and comma
927        match parse_into_statement("TQL EVAL (0, 30, '10s')           ,       data + (1 < bool 2);")
928        {
929            Statement::Tql(Tql::Eval(eval)) => {
930                assert_eq!(eval.start, "0");
931                assert_eq!(eval.end, "30");
932                assert_eq!(eval.step, "10s");
933                assert_eq!(eval.lookback, None);
934                assert_eq!(eval.query, "data + (1 < bool 2)");
935            }
936            _ => unreachable!(),
937        }
938        // query starts with a quote
939        match parse_into_statement("TQL EVAL (0, 10, '5s') '1+1';") {
940            Statement::Tql(Tql::Eval(eval)) => {
941                assert_eq!(eval.start, "0");
942                assert_eq!(eval.end, "10");
943                assert_eq!(eval.step, "5s");
944                assert_eq!(eval.lookback, None);
945                assert_eq!(eval.query, "'1+1'");
946            }
947            _ => unreachable!(),
948        }
949
950        // query starts with number
951        match parse_into_statement("TQL EVAL (300, 300, '1s') 10 atan2 20;") {
952            Statement::Tql(Tql::Eval(eval)) => {
953                assert_eq!(eval.start, "300");
954                assert_eq!(eval.end, "300");
955                assert_eq!(eval.step, "1s");
956                assert_eq!(eval.lookback, None);
957                assert_eq!(eval.query, "10 atan2 20");
958            }
959            _ => unreachable!(),
960        }
961
962        // query starts with a bracket
963        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;";
964        match parse_into_statement(sql) {
965            Statement::Tql(Tql::Eval(eval)) => {
966                assert_eq!(eval.start, "0");
967                assert_eq!(eval.end, "30");
968                assert_eq!(eval.step, "10s");
969                assert_eq!(eval.lookback, None);
970                assert_eq!(
971                    eval.query,
972                    "(sum by(host) (irate(host_cpu_seconds_total{mode!='idle'}[1m0s])) / sum by (host)((irate(host_cpu_seconds_total[1m0s])))) * 100"
973                );
974            }
975            _ => unreachable!(),
976        }
977
978        // query starts with a curly bracket
979        match parse_into_statement("TQL EVAL (0, 10, '5s') {__name__=\"test\"}") {
980            Statement::Tql(Tql::Eval(eval)) => {
981                assert_eq!(eval.start, "0");
982                assert_eq!(eval.end, "10");
983                assert_eq!(eval.step, "5s");
984                assert_eq!(eval.lookback, None);
985                assert_eq!(eval.query, "{__name__=\"test\"}");
986            }
987            _ => unreachable!(),
988        }
989    }
990
991    #[test]
992    fn test_parse_tql_error() {
993        let dialect = &GreptimeDbDialect {};
994        let parse_options = ParseOptions::default();
995
996        // invalid duration
997        let sql = "TQL EVAL (1676887657, 1676887659, 1m) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
998        let result =
999            ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1000        assert!(
1001            result
1002                .output_msg()
1003                .contains("Failed to extract a timestamp value")
1004        );
1005
1006        // missing end
1007        let sql = "TQL EVAL (1676887657, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
1008        let result =
1009            ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1010        assert!(
1011            result
1012                .output_msg()
1013                .contains("Failed to extract a timestamp value")
1014        );
1015
1016        // empty TQL query
1017        let sql = "TQL EVAL (0, 30, '10s')";
1018        let result =
1019            ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1020        assert!(result.output_msg().contains("empty TQL query"));
1021
1022        // invalid token
1023        let sql = "tql eval (0, 0, '1s) t;;';";
1024        let result =
1025            ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
1026        assert!(result.output_msg().contains("Delimiters not match"));
1027    }
1028}