feat(flow): support prom ql(in tql) in flow (#6063)

* feat: support parse prom ql in create flow

* refactor

* fix: just run tql unmodified

* refactor: determine type faster

* fix: pass original query

* tests: sqlness

* test: fix format&chore

* fix: get raw query

* test: fix sqlness randomness

* chore: what's the box for?

* test: location_to_index

* test: make sqlness more determinstic

* fix: tmp add sleep 1s after flush_flow

* undo test sleep 1s&rm done todo

* chore: more tests
This commit is contained in:
discord9
2025-05-22 11:06:09 +08:00
committed by GitHub
parent f55af5838c
commit fc6300a2ba
18 changed files with 704 additions and 146 deletions

View File

@@ -780,8 +780,77 @@ mod tests {
use super::*;
#[test]
fn test_create_flow_tql_expr() {
let sql = r#"
CREATE FLOW calc_reqs SINK TO cnt_reqs AS
TQL EVAL (0, 15, '5s') count_values("status_code", http_requests);"#;
let stmt =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap()
.pop()
.unwrap();
let Statement::CreateFlow(create_flow) = stmt else {
unreachable!()
};
let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
let to_dot_sep =
|c: TableName| format!("{}.{}.{}", c.catalog_name, c.schema_name, c.table_name);
assert_eq!("calc_reqs", expr.flow_name);
assert_eq!("greptime", expr.catalog_name);
assert_eq!(
"greptime.public.cnt_reqs",
expr.sink_table_name.map(to_dot_sep).unwrap()
);
assert!(expr.source_table_names.is_empty());
assert_eq!(
r#"TQL EVAL (0, 15, '5s') count_values("status_code", http_requests)"#,
expr.sql
);
}
#[test]
fn test_create_flow_expr() {
let sql = r"
CREATE FLOW test_distinct_basic SINK TO out_distinct_basic AS
SELECT
DISTINCT number as dis
FROM
distinct_basic;";
let stmt =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap()
.pop()
.unwrap();
let Statement::CreateFlow(create_flow) = stmt else {
unreachable!()
};
let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
let to_dot_sep =
|c: TableName| format!("{}.{}.{}", c.catalog_name, c.schema_name, c.table_name);
assert_eq!("test_distinct_basic", expr.flow_name);
assert_eq!("greptime", expr.catalog_name);
assert_eq!(
"greptime.public.out_distinct_basic",
expr.sink_table_name.map(to_dot_sep).unwrap()
);
assert_eq!(1, expr.source_table_names.len());
assert_eq!(
"greptime.public.distinct_basic",
to_dot_sep(expr.source_table_names[0].clone())
);
assert_eq!(
r"SELECT
DISTINCT number as dis
FROM
distinct_basic",
expr.sql
);
let sql = r"
CREATE FLOW `task_2`
SINK TO schema_1.table_1

View File

@@ -48,7 +48,7 @@ use lazy_static::lazy_static;
use partition::expr::{Operand, PartitionExpr, RestrictedOp};
use partition::multi_dim::MultiDimPartitionRule;
use partition::partition::{PartitionBound, PartitionDef};
use query::parser::{QueryLanguageParser, QueryStatement};
use query::parser::QueryStatement;
use query::plan::extract_and_rewrite_full_table_names;
use query::query_engine::DefaultSerializer;
use query::sql::create_table_stmt;
@@ -56,6 +56,7 @@ use regex::Regex;
use session::context::QueryContextRef;
use session::table_name::table_idents_to_full_name;
use snafu::{ensure, OptionExt, ResultExt};
use sql::parser::{ParseOptions, ParserContext};
use sql::statements::alter::{AlterDatabase, AlterTable};
use sql::statements::create::{
CreateExternalTable, CreateFlow, CreateTable, CreateTableLike, CreateView, Partitions,
@@ -440,15 +441,33 @@ impl StatementExecutor {
}
let engine = &self.query_engine;
let stmt = QueryLanguageParser::parse_sql(&expr.sql, &query_ctx)
.map_err(BoxedError::new)
.context(ExternalSnafu)?;
let plan = engine
.planner()
.plan(&stmt, query_ctx)
.await
.map_err(BoxedError::new)
.context(ExternalSnafu)?;
let stmts = ParserContext::create_with_dialect(
&expr.sql,
query_ctx.sql_dialect(),
ParseOptions::default(),
)
.map_err(BoxedError::new)
.context(ExternalSnafu)?;
ensure!(
stmts.len() == 1,
InvalidSqlSnafu {
err_msg: format!("Expect only one statement, found {}", stmts.len())
}
);
let stmt = &stmts[0];
// support tql parse too
let plan = match stmt {
// prom ql is only supported in batching mode
Statement::Tql(_) => return Ok(FlowType::Batching),
_ => engine
.planner()
.plan(&QueryStatement::Sql(stmt.clone()), query_ctx)
.await
.map_err(BoxedError::new)
.context(ExternalSnafu)?,
};
/// Visitor to find aggregation or distinct
struct FindAggr {