1use std::collections::HashMap;
16use std::sync::Arc;
17
18use chrono::{DateTime, Utc};
19use datafusion::config::ConfigOptions;
20use datafusion::error::Result as DfResult;
21use datafusion::execution::SessionStateBuilder;
22use datafusion::execution::context::SessionState;
23use datafusion::optimizer::simplify_expressions::ExprSimplifier;
24use datafusion_common::tree_node::{TreeNode, TreeNodeVisitor};
25use datafusion_common::{DFSchema, ScalarValue, TableReference};
26use datafusion_expr::simplify::SimplifyContext;
27use datafusion_expr::{AggregateUDF, Expr, HigherOrderUDF, ScalarUDF, TableSource, WindowUDF};
28use datafusion_sql::planner::{ContextProvider, SqlToRel};
29use datatypes::arrow::datatypes::DataType;
30use datatypes::schema::{
31 COLUMN_FULLTEXT_OPT_KEY_ANALYZER, COLUMN_FULLTEXT_OPT_KEY_BACKEND,
32 COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE, COLUMN_FULLTEXT_OPT_KEY_FALSE_POSITIVE_RATE,
33 COLUMN_FULLTEXT_OPT_KEY_GRANULARITY, COLUMN_SKIPPING_INDEX_OPT_KEY_FALSE_POSITIVE_RATE,
34 COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY, COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE,
35};
36use snafu::{ResultExt, ensure};
37use sqlparser::dialect::Dialect;
38use sqlparser::keywords::Keyword;
39use sqlparser::parser::Parser;
40use table::requests::{SEMANTIC_PREFIX, validate_semantic_option, validate_table_option};
41
42use crate::error::{
43 ConvertToLogicalExpressionSnafu, InvalidSqlSnafu, InvalidTableOptionSnafu, ParseSqlValueSnafu,
44 Result, SimplificationSnafu, SyntaxSnafu,
45};
46use crate::parser::{ParseOptions, ParserContext};
47use crate::parsers::with_tql_parser::CteContent;
48use crate::statements::OptionMap;
49use crate::statements::query::Query;
50use crate::statements::statement::Statement;
51use crate::util::{OptionValue, parse_option_string};
52
53pub fn is_tql(dialect: &dyn Dialect, sql: &str) -> Result<bool> {
55 let stmts = ParserContext::create_with_dialect(sql, dialect, ParseOptions::default())?;
56
57 ensure!(
58 stmts.len() == 1,
59 InvalidSqlSnafu {
60 msg: format!("Expect only one statement, found {}", stmts.len())
61 }
62 );
63 let stmt = &stmts[0];
64 match stmt {
65 Statement::Tql(_) => Ok(true),
66 Statement::Query(query) => Ok(is_simple_tql_cte_query(query)),
67 _ => Ok(false),
68 }
69}
70
71pub(crate) fn is_simple_tql_cte_query(query: &Query) -> bool {
72 use crate::parser::ParserContext;
73
74 let Some(hybrid_cte) = &query.hybrid_cte else {
75 return false;
76 };
77
78 if !has_only_hybrid_tql_cte(query) {
79 return false;
80 }
81
82 let Some(cte) = hybrid_cte.cte_tables.first() else {
83 return false;
84 };
85 if hybrid_cte.cte_tables.len() != 1 || !matches!(cte.content, CteContent::Tql(_)) {
86 return false;
87 }
88
89 let Some(reference) = extract_simple_select_star_reference(query) else {
90 return false;
91 };
92
93 let reference = ParserContext::canonicalize_identifier(reference).value;
94 let cte_name = ParserContext::canonicalize_identifier(cte.name.clone()).value;
95 reference == cte_name
96}
97
98pub(crate) fn has_tql_cte(query: &Query) -> bool {
99 query.hybrid_cte.as_ref().is_some_and(|with| {
100 with.cte_tables
101 .iter()
102 .any(|cte| matches!(cte.content, CteContent::Tql(_)))
103 })
104}
105
106fn has_only_hybrid_tql_cte(query: &Query) -> bool {
107 query
108 .inner
109 .with
110 .as_ref()
111 .is_none_or(|with| with.cte_tables.is_empty())
112}
113
114fn extract_simple_select_star_reference(query: &Query) -> Option<sqlparser::ast::Ident> {
115 use sqlparser::ast::{SetExpr, TableFactor};
116
117 if !is_plain_query_root(&query.inner) {
118 return None;
119 }
120
121 let SetExpr::Select(select) = &*query.inner.body else {
122 return None;
123 };
124 if !is_plain_select(select) || !is_plain_wildcard_projection(select.projection.as_slice()) {
125 return None;
126 }
127
128 let [table_with_joins] = select.from.as_slice() else {
129 return None;
130 };
131 if !table_with_joins.joins.is_empty() {
132 return None;
133 }
134
135 let TableFactor::Table { name, .. } = &table_with_joins.relation else {
136 return None;
137 };
138 if name.0.len() != 1 {
139 return None;
140 }
141
142 name.0[0].as_ident().cloned()
143}
144
145fn is_plain_query_root(query: &sqlparser::ast::Query) -> bool {
146 query.order_by.is_none()
147 && query.limit_clause.is_none()
148 && query.fetch.is_none()
149 && query.locks.is_empty()
150 && query.for_clause.is_none()
151 && query.settings.is_none()
152 && query.format_clause.is_none()
153 && query.pipe_operators.is_empty()
154}
155
156fn is_plain_select(select: &sqlparser::ast::Select) -> bool {
157 use sqlparser::ast::GroupByExpr;
158
159 select.distinct.is_none()
160 && select.top.is_none()
161 && select.exclude.is_none()
162 && select.into.is_none()
163 && select.lateral_views.is_empty()
164 && select.prewhere.is_none()
165 && select.selection.is_none()
166 && matches!(select.group_by, GroupByExpr::Expressions(ref exprs, _) if exprs.is_empty())
167 && select.cluster_by.is_empty()
168 && select.distribute_by.is_empty()
169 && select.sort_by.is_empty()
170 && select.having.is_none()
171 && select.named_window.is_empty()
172 && select.qualify.is_none()
173 && select.value_table_mode.is_none()
174 && select.connect_by.is_empty()
175}
176
177fn is_plain_wildcard_projection(projection: &[sqlparser::ast::SelectItem]) -> bool {
178 use sqlparser::ast::SelectItem;
179
180 matches!(
181 projection,
182 [SelectItem::Wildcard(options)]
183 if options.opt_ilike.is_none()
184 && options.opt_exclude.is_none()
185 && options.opt_except.is_none()
186 && options.opt_replace.is_none()
187 && options.opt_rename.is_none()
188 )
189}
190
191pub fn parser_expr_to_scalar_value_literal(
199 expr: sqlparser::ast::Expr,
200 require_now_expr: bool,
201) -> Result<ScalarValue> {
202 parser_expr_to_scalar_value_literal_at(expr, require_now_expr, None)
203}
204
205pub fn parser_expr_to_scalar_value_literal_at(
210 expr: sqlparser::ast::Expr,
211 require_now_expr: bool,
212 scheduled_time: Option<DateTime<Utc>>,
213) -> Result<ScalarValue> {
214 let empty_df_schema = DFSchema::empty();
216 let logical_expr = SqlToRel::new(&StubContextProvider::default())
217 .sql_to_expr(expr, &empty_df_schema, &mut Default::default())
218 .context(ConvertToLogicalExpressionSnafu)?;
219
220 struct FindNow {
221 found: bool,
222 }
223
224 impl TreeNodeVisitor<'_> for FindNow {
225 type Node = Expr;
226 fn f_down(
227 &mut self,
228 node: &Self::Node,
229 ) -> DfResult<datafusion_common::tree_node::TreeNodeRecursion> {
230 if let Expr::ScalarFunction(func) = node
231 && func.name().to_lowercase() == "now"
232 {
233 if !func.args.is_empty() {
234 return Err(datafusion_common::DataFusionError::Plan(
235 "now() function should not have arguments".to_string(),
236 ));
237 }
238 self.found = true;
239 return Ok(datafusion_common::tree_node::TreeNodeRecursion::Stop);
240 }
241 Ok(datafusion_common::tree_node::TreeNodeRecursion::Continue)
242 }
243 }
244
245 if require_now_expr {
246 let have_now = {
247 let mut visitor = FindNow { found: false };
248 logical_expr.visit(&mut visitor).unwrap();
249 visitor.found
250 };
251 if !have_now {
252 return ParseSqlValueSnafu {
253 msg: format!(
254 "expected now() expression, but not found in {}",
255 logical_expr
256 ),
257 }
258 .fail();
259 }
260 }
261
262 let info = match scheduled_time {
264 Some(dt) => SimplifyContext::builder()
265 .with_query_execution_start_time(Some(dt))
266 .build(),
267 None => SimplifyContext::builder().with_current_time().build(),
268 };
269 let simplifier = ExprSimplifier::new(info);
270
271 let logical_expr = simplifier
273 .coerce(logical_expr, &empty_df_schema)
274 .context(SimplificationSnafu)?;
275
276 let simplified_expr = simplifier
277 .simplify(logical_expr)
278 .context(SimplificationSnafu)?;
279
280 if let datafusion::logical_expr::Expr::Literal(lit, _) = simplified_expr {
281 Ok(lit)
282 } else {
283 ParseSqlValueSnafu {
285 msg: format!("expected literal value, but found {:?}", simplified_expr),
286 }
287 .fail()
288 }
289}
290
291struct StubContextProvider {
293 state: SessionState,
294}
295
296impl Default for StubContextProvider {
297 fn default() -> Self {
298 Self {
299 state: SessionStateBuilder::new()
300 .with_config(Default::default())
301 .with_runtime_env(Default::default())
302 .with_default_features()
303 .build(),
304 }
305 }
306}
307
308impl ContextProvider for StubContextProvider {
309 fn get_table_source(&self, _name: TableReference) -> DfResult<Arc<dyn TableSource>> {
310 unimplemented!()
311 }
312
313 fn get_function_meta(&self, name: &str) -> Option<Arc<ScalarUDF>> {
314 self.state.scalar_functions().get(name).cloned()
315 }
316
317 fn get_higher_order_meta(&self, name: &str) -> Option<Arc<HigherOrderUDF>> {
318 self.state.higher_order_functions().get(name).cloned()
319 }
320
321 fn get_aggregate_meta(&self, name: &str) -> Option<Arc<AggregateUDF>> {
322 self.state.aggregate_functions().get(name).cloned()
323 }
324
325 fn get_window_meta(&self, _name: &str) -> Option<Arc<WindowUDF>> {
326 unimplemented!()
327 }
328
329 fn get_variable_type(&self, _variable_names: &[String]) -> Option<DataType> {
330 unimplemented!()
331 }
332
333 fn options(&self) -> &ConfigOptions {
334 self.state.config_options()
335 }
336
337 fn udf_names(&self) -> Vec<String> {
338 self.state.scalar_functions().keys().cloned().collect()
339 }
340
341 fn higher_order_function_names(&self) -> Vec<String> {
342 self.state
343 .higher_order_functions()
344 .keys()
345 .cloned()
346 .collect()
347 }
348
349 fn udaf_names(&self) -> Vec<String> {
350 self.state.aggregate_functions().keys().cloned().collect()
351 }
352
353 fn udwf_names(&self) -> Vec<String> {
354 self.state.window_functions().keys().cloned().collect()
355 }
356}
357
358pub fn validate_column_fulltext_create_option(key: &str) -> bool {
359 [
360 COLUMN_FULLTEXT_OPT_KEY_ANALYZER,
361 COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE,
362 COLUMN_FULLTEXT_OPT_KEY_BACKEND,
363 COLUMN_FULLTEXT_OPT_KEY_GRANULARITY,
364 COLUMN_FULLTEXT_OPT_KEY_FALSE_POSITIVE_RATE,
365 ]
366 .contains(&key)
367}
368
369pub fn validate_column_skipping_index_create_option(key: &str) -> bool {
370 [
371 COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY,
372 COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE,
373 COLUMN_SKIPPING_INDEX_OPT_KEY_FALSE_POSITIVE_RATE,
374 ]
375 .contains(&key)
376}
377
378#[cfg(feature = "enterprise")]
380pub fn convert_month_day_nano_to_duration(
381 interval: arrow_buffer::IntervalMonthDayNano,
382) -> Result<std::time::Duration> {
383 let months: i64 = interval.months.into();
384 let days: i64 = interval.days.into();
385 let months_in_seconds: i64 = months * 60 * 60 * 24 * 3044 / 1000;
386 let days_in_seconds: i64 = days * 60 * 60 * 24;
387 let seconds_from_nanos = interval.nanoseconds / 1_000_000_000;
388 let total_seconds = months_in_seconds + days_in_seconds + seconds_from_nanos;
389
390 let mut nanos_remainder = interval.nanoseconds % 1_000_000_000;
391 let mut adjusted_seconds = total_seconds;
392
393 if nanos_remainder < 0 {
394 nanos_remainder += 1_000_000_000;
395 adjusted_seconds -= 1;
396 }
397
398 snafu::ensure!(
399 adjusted_seconds >= 0,
400 crate::error::InvalidIntervalSnafu {
401 reason: "must be a positive interval",
402 }
403 );
404
405 let adjusted_seconds = adjusted_seconds as u64;
407 let nanos_remainder = nanos_remainder as u32;
410
411 Ok(std::time::Duration::new(adjusted_seconds, nanos_remainder))
412}
413
414pub fn parse_with_options(parser: &mut Parser) -> Result<OptionMap> {
415 let options = parser
416 .parse_options(Keyword::WITH)
417 .context(SyntaxSnafu)?
418 .into_iter()
419 .map(parse_option_string)
420 .collect::<Result<HashMap<String, OptionValue>>>()?;
421 for (key, value) in &options {
422 if key.starts_with(SEMANTIC_PREFIX) {
423 let value = value.as_string().unwrap_or_default();
426 ensure!(
427 validate_semantic_option(key, value),
428 InvalidTableOptionSnafu { key }
429 );
430 } else {
431 ensure!(validate_table_option(key), InvalidTableOptionSnafu { key });
432 }
433 }
434 Ok(OptionMap::new(options))
435}
436
437#[cfg(test)]
438mod tests {
439 use chrono::DateTime;
440 use datafusion::functions::datetime::expr_fn::now;
441 use datafusion_expr::lit;
442 use datatypes::arrow::datatypes::TimestampNanosecondType;
443
444 use super::*;
445 use crate::dialect::GreptimeDbDialect;
446 use crate::parser::{ParseOptions, ParserContext};
447 use crate::statements::statement::Statement;
448
449 #[test]
450 fn test_is_tql() {
451 let dialect = GreptimeDbDialect {};
452
453 assert!(is_tql(&dialect, "TQL EVAL (0, 10, '1s') cpu_usage_total").unwrap());
454 assert!(!is_tql(&dialect, "SELECT 1").unwrap());
455
456 let tql_cte = r#"
457WITH tql_cte(ts, val) AS (
458 TQL EVAL (0, 15, '5s') metric
459)
460SELECT * FROM tql_cte
461"#;
462 assert!(is_tql(&dialect, tql_cte).unwrap());
463
464 let rename_cols = r#"
465WITH tql (the_timestamp, the_value) AS (
466 TQL EVAL (0, 40, '10s') metric
467)
468SELECT * FROM tql
469"#;
470 assert!(is_tql(&dialect, rename_cols).unwrap());
471 let stmts =
472 ParserContext::create_with_dialect(rename_cols, &dialect, ParseOptions::default())
473 .unwrap();
474 let Statement::Query(q) = &stmts[0] else {
475 panic!("Expected Query statement");
476 };
477 let hybrid = q.hybrid_cte.as_ref().expect("Expected hybrid cte");
478 assert_eq!(hybrid.cte_tables.len(), 1);
479 assert_eq!(hybrid.cte_tables[0].columns.len(), 2);
480 assert_eq!(hybrid.cte_tables[0].columns[0].to_string(), "the_timestamp");
481 assert_eq!(hybrid.cte_tables[0].columns[1].to_string(), "the_value");
482
483 let sql_cte = r#"
484WITH cte AS (SELECT 1)
485SELECT * FROM cte
486"#;
487 assert!(!is_tql(&dialect, sql_cte).unwrap());
488
489 let extra_sql_cte = r#"
490WITH sql_cte AS (SELECT 1), tql_cte(ts, val) AS (
491 TQL EVAL (0, 15, '5s') metric
492)
493SELECT * FROM tql_cte
494"#;
495 assert!(!is_tql(&dialect, extra_sql_cte).unwrap());
496
497 let not_select_star = r#"
498WITH tql_cte(ts, val) AS (
499 TQL EVAL (0, 15, '5s') metric
500)
501SELECT ts FROM tql_cte
502"#;
503 assert!(!is_tql(&dialect, not_select_star).unwrap());
504
505 let with_filter = r#"
506WITH tql_cte(ts, val) AS (
507 TQL EVAL (0, 15, '5s') metric
508)
509SELECT * FROM tql_cte WHERE ts > 0
510"#;
511 assert!(!is_tql(&dialect, with_filter).unwrap());
512 }
513
514 #[test]
516 fn test_simplifier() {
517 let now_time = DateTime::from_timestamp(61, 0).unwrap();
518 let lit_now = lit(ScalarValue::new_timestamp::<TimestampNanosecondType>(
519 now_time.timestamp_nanos_opt(),
520 None,
521 ));
522 let testcases = vec![
523 (now(), lit_now),
524 (now() - now(), lit(ScalarValue::DurationNanosecond(Some(0)))),
525 (
526 now() + lit(ScalarValue::new_interval_dt(0, 1500)),
527 lit(ScalarValue::new_timestamp::<TimestampNanosecondType>(
528 Some(62500000000),
529 None,
530 )),
531 ),
532 (
533 now() - (now() + lit(ScalarValue::new_interval_dt(0, 1500))),
534 lit(ScalarValue::DurationNanosecond(Some(-1500000000))),
535 ),
536 (
538 now() - now() + lit(ScalarValue::new_interval_dt(0, 1500)),
539 lit(ScalarValue::new_interval_mdn(0, 0, 1500000000)),
540 ),
541 (
542 lit(ScalarValue::new_interval_mdn(
543 0,
544 0,
545 61 * 86400 * 1_000_000_000,
546 )),
547 lit(ScalarValue::new_interval_mdn(
548 0,
549 0,
550 61 * 86400 * 1_000_000_000,
551 )),
552 ),
553 ];
554
555 let info = SimplifyContext::builder()
556 .with_query_execution_start_time(Some(now_time))
557 .build();
558 let simplifier = ExprSimplifier::new(info);
559 for (expr, expected) in testcases {
560 let expr_name = expr.schema_name().to_string();
561 let expr = simplifier.coerce(expr, &DFSchema::empty()).unwrap();
562
563 let simplified_expr = simplifier.simplify(expr).unwrap();
564 assert_eq!(
565 simplified_expr, expected,
566 "Failed to simplify expression: {expr_name}"
567 );
568 }
569 }
570}