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