1use 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
40impl 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 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 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 let index = location_to_index(sql, &start_location);
230
231 let query = &sql[index - 1..];
232 while parser.next_token() != Token::EOF {
233 }
236 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 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 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 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 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 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 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 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 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 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}