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::keywords::Keyword;
18use sqlparser::parser::ParserError;
19use sqlparser::tokenizer::Token;
20
21use crate::error::{self, Result};
22use crate::parser::ParserContext;
23use crate::parsers::utils;
24use crate::statements::statement::Statement;
25use crate::statements::tql::{Tql, TqlAnalyze, TqlEval, TqlExplain, TqlParameters};
26use crate::util::location_to_index;
27
28pub const TQL: &str = "TQL";
29const EVAL: &str = "EVAL";
30const EVALUATE: &str = "EVALUATE";
31const VERBOSE: &str = "VERBOSE";
32
33use sqlparser::parser::Parser;
34
35use crate::dialect::GreptimeDbDialect;
36use crate::parsers::error::{
37    ConvertToLogicalExpressionSnafu, EvaluationSnafu, ParserSnafu, TQLError,
38};
39
40/// TQL extension parser, including:
41/// - `TQL EVAL <query>`
42/// - `TQL EXPLAIN [VERBOSE] <query>`
43/// - `TQL ANALYZE [VERBOSE] <query>`
44impl ParserContext<'_> {
45    pub(crate) fn parse_tql(&mut self) -> Result<Statement> {
46        let _ = self.parser.next_token();
47
48        match self.parser.peek_token().token {
49            Token::Word(w) => {
50                let uppercase = w.value.to_uppercase();
51                let _consume_tql_keyword_token = self.parser.next_token();
52                match w.keyword {
53                    Keyword::NoKeyword
54                        if (uppercase == EVAL || uppercase == EVALUATE)
55                            && w.quote_style.is_none() =>
56                    {
57                        self.parse_tql_params()
58                            .map(|params| Statement::Tql(Tql::Eval(TqlEval::from(params))))
59                            .context(error::TQLSyntaxSnafu)
60                    }
61
62                    Keyword::EXPLAIN => {
63                        let is_verbose = self.has_verbose_keyword();
64                        if is_verbose {
65                            let _consume_verbose_token = self.parser.next_token();
66                        }
67                        self.parse_tql_params()
68                            .map(|mut params| {
69                                params.is_verbose = is_verbose;
70                                Statement::Tql(Tql::Explain(TqlExplain::from(params)))
71                            })
72                            .context(error::TQLSyntaxSnafu)
73                    }
74
75                    Keyword::ANALYZE => {
76                        let is_verbose = self.has_verbose_keyword();
77                        if is_verbose {
78                            let _consume_verbose_token = self.parser.next_token();
79                        }
80                        self.parse_tql_params()
81                            .map(|mut params| {
82                                params.is_verbose = is_verbose;
83                                Statement::Tql(Tql::Analyze(TqlAnalyze::from(params)))
84                            })
85                            .context(error::TQLSyntaxSnafu)
86                    }
87                    _ => self.unsupported(self.peek_token_as_string()),
88                }
89            }
90            unexpected => self.unsupported(unexpected.to_string()),
91        }
92    }
93
94    fn parse_tql_params(&mut self) -> std::result::Result<TqlParameters, TQLError> {
95        let parser = &mut self.parser;
96        let (start, end, step, lookback) = match parser.peek_token().token {
97            Token::LParen => {
98                let _consume_lparen_token = parser.next_token();
99                let start = Self::parse_string_or_number_or_word(parser, &[Token::Comma])?.0;
100                let end = Self::parse_string_or_number_or_word(parser, &[Token::Comma])?.0;
101
102                let (step, delimiter) =
103                    Self::parse_string_or_number_or_word(parser, &[Token::Comma, Token::RParen])?;
104                let lookback = if delimiter == Token::Comma {
105                    Self::parse_string_or_number_or_word(parser, &[Token::RParen])
106                        .ok()
107                        .map(|t| t.0)
108                } else {
109                    None
110                };
111
112                (start, end, step, lookback)
113            }
114            _ => ("0".to_string(), "0".to_string(), "5m".to_string(), None),
115        };
116        let query = Self::parse_tql_query(parser, self.sql).context(ParserSnafu)?;
117        Ok(TqlParameters::new(start, end, step, lookback, query))
118    }
119
120    pub fn comma_or_rparen(token: &Token) -> bool {
121        Self::is_comma(token) || Self::is_rparen(token)
122    }
123
124    #[inline]
125    fn is_comma(token: &Token) -> bool {
126        matches!(token, Token::Comma)
127    }
128
129    #[inline]
130    fn is_rparen(token: &Token) -> bool {
131        matches!(token, Token::RParen)
132    }
133
134    fn has_verbose_keyword(&mut self) -> bool {
135        self.peek_token_as_string().eq_ignore_ascii_case(VERBOSE)
136    }
137
138    /// Try to parse and consume a string, number or word token.
139    /// Return `Ok` if it's parsed and one of the given delimiter tokens is consumed.
140    /// The string and matched delimiter will be returned as a tuple.
141    fn parse_string_or_number_or_word(
142        parser: &mut Parser,
143        delimiter_tokens: &[Token],
144    ) -> std::result::Result<(String, Token), TQLError> {
145        let mut tokens = vec![];
146
147        while !delimiter_tokens.contains(&parser.peek_token().token) {
148            let token = parser.next_token().token;
149            if matches!(token, Token::EOF) {
150                break;
151            }
152            tokens.push(token);
153        }
154        let result = match tokens.len() {
155            0 => Err(ParserError::ParserError(
156                "Expected at least one token".to_string(),
157            ))
158            .context(ParserSnafu),
159            1 => {
160                let value = match tokens[0].clone() {
161                    Token::Number(n, _) => n,
162                    Token::DoubleQuotedString(s) | Token::SingleQuotedString(s) => s,
163                    Token::Word(_) => Self::parse_tokens_to_ts(tokens)?,
164                    unexpected => {
165                        return Err(ParserError::ParserError(format!(
166                            "Expected number, string or word, but have {unexpected:?}"
167                        )))
168                        .context(ParserSnafu);
169                    }
170                };
171                Ok(value)
172            }
173            _ => Self::parse_tokens_to_ts(tokens),
174        };
175        for token in delimiter_tokens {
176            if parser.consume_token(token) {
177                return result.map(|v| (v, token.clone()));
178            }
179        }
180        Err(ParserError::ParserError(format!(
181            "Delimiters not match {delimiter_tokens:?}"
182        )))
183        .context(ParserSnafu)
184    }
185
186    /// Parse the tokens to seconds and convert to string.
187    fn parse_tokens_to_ts(tokens: Vec<Token>) -> std::result::Result<String, TQLError> {
188        let parser_expr = Self::parse_to_expr(tokens)?;
189        let lit = utils::parser_expr_to_scalar_value_literal(parser_expr)
190            .map_err(Box::new)
191            .context(ConvertToLogicalExpressionSnafu)?;
192
193        let second = match lit {
194            ScalarValue::TimestampNanosecond(ts_nanos, _)
195            | ScalarValue::DurationNanosecond(ts_nanos) => ts_nanos.map(|v| v / 1_000_000_000),
196            ScalarValue::TimestampMicrosecond(ts_micros, _)
197            | ScalarValue::DurationMicrosecond(ts_micros) => ts_micros.map(|v| v / 1_000_000),
198            ScalarValue::TimestampMillisecond(ts_millis, _)
199            | ScalarValue::DurationMillisecond(ts_millis) => ts_millis.map(|v| v / 1_000),
200            ScalarValue::TimestampSecond(ts_secs, _) | ScalarValue::DurationSecond(ts_secs) => {
201                ts_secs
202            }
203            _ => None,
204        };
205
206        second.map(|ts| ts.to_string()).context(EvaluationSnafu {
207            msg: format!("Failed to extract a timestamp value {lit:?}"),
208        })
209    }
210
211    fn parse_to_expr(tokens: Vec<Token>) -> std::result::Result<sqlparser::ast::Expr, TQLError> {
212        Parser::new(&GreptimeDbDialect {})
213            .with_tokens(tokens)
214            .parse_expr()
215            .context(ParserSnafu)
216    }
217
218    fn parse_tql_query(parser: &mut Parser, sql: &str) -> std::result::Result<String, ParserError> {
219        while matches!(parser.peek_token().token, Token::Comma) {
220            let _skip_token = parser.next_token();
221        }
222        let start_tql = parser.next_token();
223        if start_tql == Token::EOF {
224            return Err(ParserError::ParserError("empty TQL query".to_string()));
225        }
226
227        let start_location = start_tql.span.start;
228        // translate the start location to the index in the sql string
229        let index = location_to_index(sql, &start_location);
230
231        let query = &sql[index - 1..];
232        while parser.next_token() != Token::EOF {
233            // consume all tokens
234            // TODO(dennis): supports multi TQL statements separated by ';'?
235        }
236        // remove the last ';' or tailing space if exists
237        Ok(query.trim().trim_end_matches(';').to_string())
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use common_error::ext::ErrorExt;
244
245    use super::*;
246    use crate::dialect::GreptimeDbDialect;
247    use crate::parser::ParseOptions;
248
249    fn parse_into_statement(sql: &str) -> Statement {
250        let mut result =
251            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
252                .unwrap();
253        assert_eq!(1, result.len());
254        result.remove(0)
255    }
256
257    #[test]
258    fn test_parse_tql_eval_with_functions() {
259        let sql = "TQL EVAL (now() - now(), now() -  (now() - '10 seconds'::interval), '1s') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
260        let statement = parse_into_statement(sql);
261        match statement {
262            Statement::Tql(Tql::Eval(eval)) => {
263                assert_eq!(eval.start, "0");
264                assert_eq!(eval.end, "10");
265                assert_eq!(eval.step, "1s");
266                assert_eq!(eval.lookback, None);
267                assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
268            }
269            _ => unreachable!(),
270        }
271
272        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";
273        match parse_into_statement(sql) {
274            Statement::Tql(Tql::Eval(eval)) => {
275                assert_eq!(eval.start, "300");
276                assert_eq!(eval.end, "1200");
277                assert_eq!(eval.step, "1m");
278                assert_eq!(eval.lookback, None);
279                assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
280            }
281            _ => unreachable!(),
282        }
283
284        let sql = "TQL EVAL (now(), now()-'5m', '30s') http_requests_total";
285        let result =
286            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
287        assert!(result.is_err());
288    }
289
290    #[test]
291    fn test_parse_tql_eval() {
292        let sql = "TQL EVAL (1676887657, 1676887659, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
293        match parse_into_statement(sql) {
294            Statement::Tql(Tql::Eval(eval)) => {
295                assert_eq!(eval.start, "1676887657");
296                assert_eq!(eval.end, "1676887659");
297                assert_eq!(eval.step, "1m");
298                assert_eq!(eval.lookback, None);
299                assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
300            }
301            _ => unreachable!(),
302        }
303
304        let sql = "TQL EVAL (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
305        let statement = parse_into_statement(sql);
306
307        match &statement {
308            Statement::Tql(Tql::Eval(eval)) => {
309                assert_eq!(eval.start, "1676887657.1");
310                assert_eq!(eval.end, "1676887659.5");
311                assert_eq!(eval.step, "30.3");
312                assert_eq!(eval.lookback, None);
313                assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
314            }
315            _ => unreachable!(),
316        }
317
318        let sql2 = "TQL EVALUATE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
319        let statement2 = parse_into_statement(sql2);
320        assert_eq!(statement, statement2);
321
322        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";
323        match parse_into_statement(sql) {
324            Statement::Tql(Tql::Eval(eval)) => {
325                assert_eq!(eval.start, "2015-07-01T20:10:30.781Z");
326                assert_eq!(eval.end, "2015-07-01T20:11:00.781Z");
327                assert_eq!(eval.step, "30s");
328                assert_eq!(eval.lookback, None);
329                assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
330            }
331            _ => unreachable!(),
332        }
333    }
334
335    #[test]
336    fn test_parse_tql_with_lookback_values() {
337        let sql = "TQL EVAL (1676887657, 1676887659, '1m', '5m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
338        match parse_into_statement(sql) {
339            Statement::Tql(Tql::Eval(eval)) => {
340                assert_eq!(eval.start, "1676887657");
341                assert_eq!(eval.end, "1676887659");
342                assert_eq!(eval.step, "1m".to_string());
343                assert_eq!(eval.lookback, Some("5m".to_string()));
344                assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
345            }
346            _ => unreachable!(),
347        }
348
349        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";
350        match parse_into_statement(sql) {
351            Statement::Tql(Tql::Eval(eval)) => {
352                assert_eq!(eval.start, "300");
353                assert_eq!(eval.end, "1200");
354                assert_eq!(eval.step, "1m");
355                assert_eq!(eval.lookback, Some("7m".to_string()));
356                assert_eq!(eval.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
357            }
358            _ => unreachable!(),
359        }
360
361        let sql = "TQL EXPLAIN (20, 100, 10, '3m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
362        match parse_into_statement(sql) {
363            Statement::Tql(Tql::Explain(explain)) => {
364                assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
365                assert_eq!(explain.start, "20");
366                assert_eq!(explain.end, "100");
367                assert_eq!(explain.step, "10");
368                assert_eq!(explain.lookback, Some("3m".to_string()));
369                assert!(!explain.is_verbose);
370            }
371            _ => unreachable!(),
372        }
373
374        let sql = "TQL EXPLAIN VERBOSE (20, 100, 10, '3m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
375        match parse_into_statement(sql) {
376            Statement::Tql(Tql::Explain(explain)) => {
377                assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
378                assert_eq!(explain.start, "20");
379                assert_eq!(explain.end, "100");
380                assert_eq!(explain.step, "10");
381                assert_eq!(explain.lookback, Some("3m".to_string()));
382                assert!(explain.is_verbose);
383            }
384            _ => unreachable!(),
385        }
386
387        let sql = "TQL ANALYZE (1676887657, 1676887659, '1m', '9m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
388        match parse_into_statement(sql) {
389            Statement::Tql(Tql::Analyze(analyze)) => {
390                assert_eq!(analyze.start, "1676887657");
391                assert_eq!(analyze.end, "1676887659");
392                assert_eq!(analyze.step, "1m");
393                assert_eq!(analyze.lookback, Some("9m".to_string()));
394                assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
395                assert!(!analyze.is_verbose);
396            }
397            _ => unreachable!(),
398        }
399
400        let sql = "TQL ANALYZE VERBOSE (1676887657, 1676887659, '1m', '9m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
401        match parse_into_statement(sql) {
402            Statement::Tql(Tql::Analyze(analyze)) => {
403                assert_eq!(analyze.start, "1676887657");
404                assert_eq!(analyze.end, "1676887659");
405                assert_eq!(analyze.step, "1m");
406                assert_eq!(analyze.lookback, Some("9m".to_string()));
407                assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
408                assert!(analyze.is_verbose);
409            }
410            _ => unreachable!(),
411        }
412    }
413
414    #[test]
415    fn test_parse_tql_explain() {
416        let sql = "TQL EXPLAIN http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
417        match parse_into_statement(sql) {
418            Statement::Tql(Tql::Explain(explain)) => {
419                assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
420                assert_eq!(explain.start, "0");
421                assert_eq!(explain.end, "0");
422                assert_eq!(explain.step, "5m");
423                assert_eq!(explain.lookback, None);
424                assert!(!explain.is_verbose);
425            }
426            _ => unreachable!(),
427        }
428
429        let sql = "TQL EXPLAIN VERBOSE http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
430        match parse_into_statement(sql) {
431            Statement::Tql(Tql::Explain(explain)) => {
432                assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
433                assert_eq!(explain.start, "0");
434                assert_eq!(explain.end, "0");
435                assert_eq!(explain.step, "5m");
436                assert_eq!(explain.lookback, None);
437                assert!(explain.is_verbose);
438            }
439            _ => unreachable!(),
440        }
441
442        let sql = "TQL EXPLAIN (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
443        match parse_into_statement(sql) {
444            Statement::Tql(Tql::Explain(explain)) => {
445                assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
446                assert_eq!(explain.start, "20");
447                assert_eq!(explain.end, "100");
448                assert_eq!(explain.step, "10");
449                assert_eq!(explain.lookback, None);
450                assert!(!explain.is_verbose);
451            }
452            _ => unreachable!(),
453        }
454
455        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";
456        match parse_into_statement(sql) {
457            Statement::Tql(Tql::Explain(explain)) => {
458                assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
459                assert_eq!(explain.start, "300");
460                assert_eq!(explain.end, "1200");
461                assert_eq!(explain.step, "10");
462                assert_eq!(explain.lookback, None);
463                assert!(!explain.is_verbose);
464            }
465            _ => unreachable!(),
466        }
467
468        let sql = "TQL EXPLAIN VERBOSE (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
469        match parse_into_statement(sql) {
470            Statement::Tql(Tql::Explain(explain)) => {
471                assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
472                assert_eq!(explain.start, "20");
473                assert_eq!(explain.end, "100");
474                assert_eq!(explain.step, "10");
475                assert_eq!(explain.lookback, None);
476                assert!(explain.is_verbose);
477            }
478            _ => unreachable!(),
479        }
480
481        let sql = "TQL EXPLAIN verbose (20,100,10) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
482        match parse_into_statement(sql) {
483            Statement::Tql(Tql::Explain(explain)) => {
484                assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
485                assert_eq!(explain.start, "20");
486                assert_eq!(explain.end, "100");
487                assert_eq!(explain.step, "10");
488                assert_eq!(explain.lookback, None);
489                assert!(explain.is_verbose);
490            }
491            _ => unreachable!(),
492        }
493
494        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";
495        match parse_into_statement(sql) {
496            Statement::Tql(Tql::Explain(explain)) => {
497                assert_eq!(explain.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
498                assert_eq!(explain.start, "300");
499                assert_eq!(explain.end, "1200");
500                assert_eq!(explain.step, "10");
501                assert_eq!(explain.lookback, None);
502                assert!(explain.is_verbose);
503            }
504            _ => unreachable!(),
505        }
506    }
507
508    #[test]
509    fn test_parse_tql_analyze() {
510        let sql = "TQL ANALYZE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
511        match parse_into_statement(sql) {
512            Statement::Tql(Tql::Analyze(analyze)) => {
513                assert_eq!(analyze.start, "1676887657.1");
514                assert_eq!(analyze.end, "1676887659.5");
515                assert_eq!(analyze.step, "30.3");
516                assert_eq!(analyze.lookback, None);
517                assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
518                assert!(!analyze.is_verbose);
519            }
520            _ => unreachable!(),
521        }
522
523        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";
524        match parse_into_statement(sql) {
525            Statement::Tql(Tql::Analyze(analyze)) => {
526                assert_eq!(analyze.start, "300");
527                assert_eq!(analyze.end, "1200");
528                assert_eq!(analyze.step, "10");
529                assert_eq!(analyze.lookback, None);
530                assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
531                assert!(!analyze.is_verbose);
532            }
533            _ => unreachable!(),
534        }
535
536        let sql = "TQL ANALYZE VERBOSE (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
537        match parse_into_statement(sql) {
538            Statement::Tql(Tql::Analyze(analyze)) => {
539                assert_eq!(analyze.start, "1676887657.1");
540                assert_eq!(analyze.end, "1676887659.5");
541                assert_eq!(analyze.step, "30.3");
542                assert_eq!(analyze.lookback, None);
543                assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
544                assert!(analyze.is_verbose);
545            }
546            _ => unreachable!(),
547        }
548
549        let sql = "TQL ANALYZE verbose (1676887657.1, 1676887659.5, 30.3) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
550        match parse_into_statement(sql) {
551            Statement::Tql(Tql::Analyze(analyze)) => {
552                assert_eq!(analyze.start, "1676887657.1");
553                assert_eq!(analyze.end, "1676887659.5");
554                assert_eq!(analyze.step, "30.3");
555                assert_eq!(analyze.lookback, None);
556                assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
557                assert!(analyze.is_verbose);
558            }
559            _ => unreachable!(),
560        }
561
562        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";
563        match parse_into_statement(sql) {
564            Statement::Tql(Tql::Analyze(analyze)) => {
565                assert_eq!(analyze.start, "300");
566                assert_eq!(analyze.end, "1200");
567                assert_eq!(analyze.step, "10");
568                assert_eq!(analyze.lookback, None);
569                assert_eq!(analyze.query, "http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m");
570                assert!(analyze.is_verbose);
571            }
572            _ => unreachable!(),
573        }
574    }
575
576    #[test]
577    fn test_parse_tql_with_various_queries() {
578        // query has whitespaces and comma
579        match parse_into_statement("TQL EVAL (0, 30, '10s')           ,       data + (1 < bool 2);")
580        {
581            Statement::Tql(Tql::Eval(eval)) => {
582                assert_eq!(eval.start, "0");
583                assert_eq!(eval.end, "30");
584                assert_eq!(eval.step, "10s");
585                assert_eq!(eval.lookback, None);
586                assert_eq!(eval.query, "data + (1 < bool 2)");
587            }
588            _ => unreachable!(),
589        }
590        // query starts with a quote
591        match parse_into_statement("TQL EVAL (0, 10, '5s') '1+1';") {
592            Statement::Tql(Tql::Eval(eval)) => {
593                assert_eq!(eval.start, "0");
594                assert_eq!(eval.end, "10");
595                assert_eq!(eval.step, "5s");
596                assert_eq!(eval.lookback, None);
597                assert_eq!(eval.query, "'1+1'");
598            }
599            _ => unreachable!(),
600        }
601
602        // query starts with number
603        match parse_into_statement("TQL EVAL (300, 300, '1s') 10 atan2 20;") {
604            Statement::Tql(Tql::Eval(eval)) => {
605                assert_eq!(eval.start, "300");
606                assert_eq!(eval.end, "300");
607                assert_eq!(eval.step, "1s");
608                assert_eq!(eval.lookback, None);
609                assert_eq!(eval.query, "10 atan2 20");
610            }
611            _ => unreachable!(),
612        }
613
614        // query starts with a bracket
615        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;";
616        match parse_into_statement(sql) {
617            Statement::Tql(Tql::Eval(eval)) => {
618                assert_eq!(eval.start, "0");
619                assert_eq!(eval.end, "30");
620                assert_eq!(eval.step, "10s");
621                assert_eq!(eval.lookback, None);
622                assert_eq!(eval.query, "(sum by(host) (irate(host_cpu_seconds_total{mode!='idle'}[1m0s])) / sum by (host)((irate(host_cpu_seconds_total[1m0s])))) * 100");
623            }
624            _ => unreachable!(),
625        }
626
627        // query starts with a curly bracket
628        match parse_into_statement("TQL EVAL (0, 10, '5s') {__name__=\"test\"}") {
629            Statement::Tql(Tql::Eval(eval)) => {
630                assert_eq!(eval.start, "0");
631                assert_eq!(eval.end, "10");
632                assert_eq!(eval.step, "5s");
633                assert_eq!(eval.lookback, None);
634                assert_eq!(eval.query, "{__name__=\"test\"}");
635            }
636            _ => unreachable!(),
637        }
638    }
639
640    #[test]
641    fn test_parse_tql_error() {
642        let dialect = &GreptimeDbDialect {};
643        let parse_options = ParseOptions::default();
644
645        // invalid duration
646        let sql = "TQL EVAL (1676887657, 1676887659, 1m) http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
647        let result =
648            ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
649        assert!(result
650            .output_msg()
651            .contains("Failed to extract a timestamp value"));
652
653        // missing end
654        let sql = "TQL EVAL (1676887657, '1m') http_requests_total{environment=~'staging|testing|development',method!='GET'} @ 1609746000 offset 5m";
655        let result =
656            ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
657        assert!(result
658            .output_msg()
659            .contains("Failed to extract a timestamp value"));
660
661        // empty TQL query
662        let sql = "TQL EVAL (0, 30, '10s')";
663        let result =
664            ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
665        assert!(result.output_msg().contains("empty TQL query"));
666
667        // invalid token
668        let sql = "tql eval (0, 0, '1s) t;;';";
669        let result =
670            ParserContext::create_with_dialect(sql, dialect, parse_options.clone()).unwrap_err();
671        assert!(result.output_msg().contains("Delimiters not match"));
672    }
673}