Skip to content

Commit fa252c8

Browse files
authored
feat(script): add script compiler (#15113)
* feat(script): add script compiler * fix * fix * fix * fix * fix * add executor * add timeout check * fix
1 parent 7452ffc commit fa252c8

File tree

24 files changed

+3766
-153
lines changed

24 files changed

+3766
-153
lines changed

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ members = [
4646
"src/query/pipeline/sinks",
4747
"src/query/pipeline/sources",
4848
"src/query/pipeline/transforms",
49+
"src/query/script",
4950
"src/query/settings",
5051
"src/query/sql",
5152
"src/query/storages/common/blocks",

src/common/exception/src/exception_code.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,9 @@ build_exceptions! {
352352
TenantQuotaUnknown(2902),
353353
TenantQuotaExceeded(2903),
354354

355+
// Script error codes.
356+
ScriptSemanticError(3001),
357+
ScriptExecutionError(3002),
355358
}
356359

357360
// Storage errors [3001, 4000].

src/query/ast/src/ast/query.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -320,10 +320,10 @@ pub enum SetOperator {
320320
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
321321
pub struct OrderByExpr {
322322
pub expr: Expr,
323-
// Optional `ASC` or `DESC`
323+
/// `ASC` or `DESC`
324324
#[drive(skip)]
325325
pub asc: Option<bool>,
326-
// Optional `NULLS FIRST` or `NULLS LAST`
326+
/// `NULLS FIRST` or `NULLS LAST`
327327
#[drive(skip)]
328328
pub nulls_first: Option<bool>,
329329
}
@@ -352,16 +352,16 @@ impl Display for OrderByExpr {
352352
/// One item of the comma-separated list following `SELECT`
353353
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
354354
pub enum SelectTarget {
355-
// Expression with alias, e.g. `SELECT t.a, b AS a, a+1 AS b FROM t`
355+
/// Expression with alias, e.g. `SELECT t.a, b AS a, a+1 AS b FROM t`
356356
AliasedExpr {
357357
expr: Box<Expr>,
358358
alias: Option<Identifier>,
359359
},
360360

361-
// Qualified star name, e.g. `SELECT t.* exclude a, columns(expr) FROM t`.
362-
// Columns("pattern_str")
363-
// Columns(lambda expression)
364-
// For simplicity, star wildcard is involved.
361+
/// Qualified star name, e.g. `SELECT t.* exclude a, columns(expr) FROM t`.
362+
/// Columns("pattern_str")
363+
/// Columns(lambda expression)
364+
/// For simplicity, star wildcard is involved.
365365
StarColumns {
366366
qualified: QualifiedName,
367367
column_filter: Option<ColumnFilter>,

src/query/ast/src/ast/statements/script.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use databend_common_exception::Span;
1919

2020
use crate::ast::Expr;
2121
use crate::ast::Identifier;
22-
use crate::ast::Query;
2322
use crate::ast::Statement;
2423
use crate::ast::TypeName;
2524

@@ -64,15 +63,15 @@ impl Display for VariableDeclare {
6463
}
6564

6665
#[derive(Debug, Clone, PartialEq)]
67-
pub struct QueryDeclare {
66+
pub struct StatementDeclare {
6867
pub name: Identifier,
69-
pub query: Query,
68+
pub stmt: Statement,
7069
}
7170

72-
impl Display for QueryDeclare {
71+
impl Display for StatementDeclare {
7372
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
74-
let QueryDeclare { name, query } = self;
75-
write!(f, "{name} RESULTSET := {query}")
73+
let StatementDeclare { name, stmt } = self;
74+
write!(f, "{name} RESULTSET := {stmt}")
7675
}
7776
}
7877

@@ -82,9 +81,13 @@ pub enum ScriptStatement {
8281
span: Span,
8382
declare: VariableDeclare,
8483
},
85-
LetQuery {
84+
LetStatement {
8685
span: Span,
87-
declare: QueryDeclare,
86+
declare: StatementDeclare,
87+
},
88+
RunStatement {
89+
span: Span,
90+
stmt: Statement,
8891
},
8992
Assign {
9093
span: Span,
@@ -149,17 +152,14 @@ pub enum ScriptStatement {
149152
results: Vec<Vec<ScriptStatement>>,
150153
else_result: Option<Vec<ScriptStatement>>,
151154
},
152-
SQLStatement {
153-
span: Span,
154-
stmt: Statement,
155-
},
156155
}
157156

158157
impl Display for ScriptStatement {
159158
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
160159
match self {
161160
ScriptStatement::LetVar { declare, .. } => write!(f, "LET {declare}"),
162-
ScriptStatement::LetQuery { declare, .. } => write!(f, "LET {declare}"),
161+
ScriptStatement::LetStatement { declare, .. } => write!(f, "LET {declare}"),
162+
ScriptStatement::RunStatement { stmt, .. } => write!(f, "{stmt}"),
163163
ScriptStatement::Assign { name, value, .. } => write!(f, "{name} := {value}"),
164164
ScriptStatement::Return { value, .. } => {
165165
if let Some(value) = value {
@@ -352,7 +352,6 @@ impl Display for ScriptStatement {
352352
}
353353
write!(f, "END IF")
354354
}
355-
ScriptStatement::SQLStatement { stmt, .. } => write!(f, "{stmt}"),
356355
}
357356
}
358357
}

src/query/ast/src/parser/expr.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,21 +1480,17 @@ pub fn literal_string(i: Input) -> IResult<String> {
14801480
QuotedString
14811481
},
14821482
|token| {
1483-
if token
1484-
.text()
1485-
.chars()
1486-
.next()
1487-
.filter(|c| i.dialect.is_string_quote(*c))
1488-
.is_some()
1489-
{
1490-
let str = &token.text()[1..token.text().len() - 1];
1491-
let unescaped = unescape_string(str, '\'').ok_or(nom::Err::Failure(
1492-
ErrorKind::Other("invalid escape or unicode"),
1493-
))?;
1494-
Ok(unescaped)
1495-
} else {
1496-
Err(nom::Err::Error(ErrorKind::ExpectToken(QuotedString)))
1483+
if let Some(quote) = token.text().chars().next() {
1484+
if i.dialect.is_string_quote(quote) {
1485+
let str = &token.text()[1..token.text().len() - 1];
1486+
let unescaped = unescape_string(str, quote).ok_or(nom::Err::Failure(
1487+
ErrorKind::Other("invalid escape or unicode"),
1488+
))?;
1489+
return Ok(unescaped);
1490+
}
14971491
}
1492+
1493+
Err(nom::Err::Error(ErrorKind::ExpectToken(QuotedString)))
14981494
},
14991495
)(i)
15001496
}

src/query/ast/src/parser/script.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,22 @@ use crate::ast::*;
1919
use crate::parser::common::*;
2020
use crate::parser::expr::*;
2121
use crate::parser::input::Input;
22-
use crate::parser::query::*;
2322
use crate::parser::statement::*;
2423
use crate::parser::token::*;
2524
use crate::rule;
2625

26+
pub fn script_stmts(i: Input) -> IResult<Vec<ScriptStatement>> {
27+
semicolon_terminated_list1(script_stmt)(i)
28+
}
29+
2730
pub fn script_stmt(i: Input) -> IResult<ScriptStatement> {
28-
let let_query_stmt = map(
31+
let let_stmt_stmt = map(
2932
consumed(rule! {
30-
LET ~^#ident ~ RESULTSET ~ ^":=" ~ ^#query
33+
LET ~^#ident ~ RESULTSET ~ ^":=" ~ ^#statement_body
3134
}),
32-
|(span, (_, name, _, _, query))| ScriptStatement::LetQuery {
35+
|(span, (_, name, _, _, stmt))| ScriptStatement::LetStatement {
3336
span: transform_span(span.tokens),
34-
declare: QueryDeclare { name, query },
37+
declare: StatementDeclare { name, stmt },
3538
},
3639
);
3740
let let_var_stmt = map(
@@ -47,6 +50,15 @@ pub fn script_stmt(i: Input) -> IResult<ScriptStatement> {
4750
},
4851
},
4952
);
53+
let run_stmt = map(
54+
consumed(rule! {
55+
#statement_body
56+
}),
57+
|(span, stmt)| ScriptStatement::RunStatement {
58+
span: transform_span(span.tokens),
59+
stmt,
60+
},
61+
);
5062
let assign_stmt = map(
5163
consumed(rule! {
5264
#ident ~ ":=" ~ ^#expr
@@ -199,19 +211,11 @@ pub fn script_stmt(i: Input) -> IResult<ScriptStatement> {
199211
}
200212
},
201213
);
202-
let sql_stmt = map(
203-
consumed(rule! {
204-
#statement_body
205-
}),
206-
|(span, stmt)| ScriptStatement::SQLStatement {
207-
span: transform_span(span.tokens),
208-
stmt,
209-
},
210-
);
211214

212215
rule!(
213-
#let_query_stmt
216+
#let_stmt_stmt
214217
| #let_var_stmt
218+
| #run_stmt
215219
| #assign_stmt
216220
| #return_stmt
217221
| #for_loop_stmt
@@ -223,6 +227,5 @@ pub fn script_stmt(i: Input) -> IResult<ScriptStatement> {
223227
| #continue_stmt
224228
| #case_stmt
225229
| #if_stmt
226-
| #sql_stmt
227230
)(i)
228231
}

src/query/ast/src/parser/unescape.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub fn unescape_string(s: &str, quote: char) -> Option<String> {
2828
Some('r') => s.push('\r'),
2929
Some('t') => s.push('\t'),
3030
Some('\'') => s.push('\''),
31+
Some('"') => s.push('"'),
3132
Some('\\') => s.push('\\'),
3233
Some('u') => s.push(unescape_unicode(&mut chars)?),
3334
Some('x') => s.push(unescape_byte(&mut chars)?),

src/query/ast/tests/it/parser.rs

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ fn test_statement_error() {
782782
#[test]
783783
fn test_raw_insert_stmt() {
784784
let mut mint = Mint::new("tests/it/testdata");
785-
let file = &mut mint.new_goldenfile("raw_insert.txt").unwrap();
785+
let file = &mut mint.new_goldenfile("raw-insert.txt").unwrap();
786786
let cases = &[
787787
r#"insert into t (c1, c2) values (1, 2), (3, 4);"#,
788788
r#"insert into t (c1, c2) values (1, 2);"#,
@@ -978,48 +978,72 @@ fn test_expr() {
978978
}
979979

980980
#[test]
981-
fn test_experimental_expr() {
981+
fn test_expr_error() {
982982
let mut mint = Mint::new("tests/it/testdata");
983-
let file = &mut mint.new_goldenfile("experimental-expr.txt").unwrap();
983+
let file = &mut mint.new_goldenfile("expr-error.txt").unwrap();
984984

985985
let cases = &[
986-
r#"a"#,
986+
r#"5 * (a and ) 1"#,
987+
r#"a + +"#,
988+
r#"CAST(col1 AS foo)"#,
989+
r#"1 a"#,
990+
r#"CAST(col1)"#,
987991
r#"a.add(b)"#,
988-
r#"a.sub(b).add(e)"#,
989-
r#"a.sub(b).add(e)"#,
990-
r#"1 + {'k1': 4}.k1"#,
991-
r#"'3'.plus(4)"#,
992-
r#"(3).add({'k1': 4 }.k1)"#,
993992
r#"[ x * 100 FOR x in [1,2,3] if x % 2 = 0 ]"#,
993+
r#"
994+
G.E.B IS NOT NULL
995+
AND col1 NOT BETWEEN col2 AND
996+
AND 1 + col3 DIV sum(col4)
997+
"#,
994998
];
995999

9961000
for case in cases {
997-
run_parser_with_dialect(file, expr, Dialect::Experimental, ParseMode::Default, case);
1001+
run_parser(file, expr, case);
9981002
}
9991003
}
10001004

10011005
#[test]
1002-
fn test_expr_error() {
1006+
fn test_dialect() {
10031007
let mut mint = Mint::new("tests/it/testdata");
1004-
let file = &mut mint.new_goldenfile("expr-error.txt").unwrap();
1008+
let file = &mut mint.new_goldenfile("dialect.txt").unwrap();
10051009

10061010
let cases = &[
1007-
r#"5 * (a and ) 1"#,
1008-
r#"a + +"#,
1009-
r#"CAST(col1 AS foo)"#,
1010-
r#"1 a"#,
1011-
r#"CAST(col1)"#,
1011+
r#"'a'"#,
1012+
r#""a""#,
1013+
r#"`a`"#,
1014+
r#"'a''b'"#,
1015+
r#"'a""b'"#,
1016+
r#"'a\'b'"#,
1017+
r#"'a"b'"#,
1018+
r#"'a`b'"#,
1019+
r#""a''b""#,
1020+
r#""a""b""#,
1021+
r#""a'b""#,
1022+
r#""a\"b""#,
1023+
r#""a`b""#,
1024+
];
1025+
1026+
for case in cases {
1027+
run_parser_with_dialect(file, expr, Dialect::PostgreSQL, ParseMode::Default, case);
1028+
}
1029+
1030+
for case in cases {
1031+
run_parser_with_dialect(file, expr, Dialect::MySQL, ParseMode::Default, case);
1032+
}
1033+
1034+
let cases = &[
1035+
r#"a"#,
10121036
r#"a.add(b)"#,
1037+
r#"a.sub(b).add(e)"#,
1038+
r#"a.sub(b).add(e)"#,
1039+
r#"1 + {'k1': 4}.k1"#,
1040+
r#"'3'.plus(4)"#,
1041+
r#"(3).add({'k1': 4 }.k1)"#,
10131042
r#"[ x * 100 FOR x in [1,2,3] if x % 2 = 0 ]"#,
1014-
r#"
1015-
G.E.B IS NOT NULL AND
1016-
col1 NOT BETWEEN col2 AND
1017-
AND 1 + col3 DIV sum(col4)
1018-
"#,
10191043
];
10201044

10211045
for case in cases {
1022-
run_parser(file, expr, case);
1046+
run_parser_with_dialect(file, expr, Dialect::Experimental, ParseMode::Default, case);
10231047
}
10241048
}
10251049

0 commit comments

Comments
 (0)