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