diff --git a/Cargo.lock b/Cargo.lock index 3f3d8d8764817..62a8dcda6f972 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2610,6 +2610,7 @@ dependencies = [ "ethnum 1.5.0 (git+https://github.com/ariesdevil/ethnum-rs?rev=4cb05f1)", "fast-float", "goldenfile", + "indent", "itertools 0.10.5", "logos", "minitrace", @@ -2623,6 +2624,7 @@ dependencies = [ "strsim 0.10.0", "strum 0.24.1", "strum_macros 0.24.3", + "unindent", "url", ] @@ -7812,6 +7814,12 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indent" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f1a0777d972970f204fdf8ef319f1f4f8459131636d7e3c96c5d59570d0fa6" + [[package]] name = "indexmap" version = "1.9.3" @@ -8324,7 +8332,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.4", ] [[package]] diff --git a/src/query/ast/Cargo.toml b/src/query/ast/Cargo.toml index 446fe7c706b72..524303bf0e94b 100644 --- a/src/query/ast/Cargo.toml +++ b/src/query/ast/Cargo.toml @@ -21,6 +21,7 @@ derive-visitor = { workspace = true } enum-as-inner = "0.5.1" ethnum = { workspace = true } fast-float = "0.2.0" +indent = "0.1.1" itertools = { workspace = true } logos = "0.12.1" minitrace = { workspace = true } @@ -40,6 +41,7 @@ databend-common-base = { path = "../../common/base" } goldenfile = "1.4" pretty_assertions = "1.3.0" regex = { workspace = true } +unindent = "0.2.3" [[bench]] name = "bench" diff --git a/src/query/ast/src/ast/statements/mod.rs b/src/query/ast/src/ast/statements/mod.rs index e4a2118d7a55b..66b23c831bb9c 100644 --- a/src/query/ast/src/ast/statements/mod.rs +++ b/src/query/ast/src/ast/statements/mod.rs @@ -33,6 +33,7 @@ mod password_policy; mod pipe; mod presign; mod replace; +mod script; mod share; mod show; mod stage; @@ -68,6 +69,7 @@ pub use password_policy::*; pub use pipe::*; pub use presign::*; pub use replace::*; +pub use script::*; pub use share::*; pub use show::*; pub use stage::*; diff --git a/src/query/ast/src/ast/statements/script.rs b/src/query/ast/src/ast/statements/script.rs new file mode 100644 index 0000000000000..3e4ca5bae7f03 --- /dev/null +++ b/src/query/ast/src/ast/statements/script.rs @@ -0,0 +1,358 @@ +// Copyright 2021 Datafuse Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Display; +use std::fmt::Formatter; + +use databend_common_exception::Span; + +use crate::ast::Expr; +use crate::ast::Identifier; +use crate::ast::Query; +use crate::ast::Statement; +use crate::ast::TypeName; + +const INDENT_DEPTH: usize = 4; + +#[derive(Debug, Clone, PartialEq)] +pub struct CreateStoredProceduer { + pub or_replace: bool, + pub name: Identifier, + pub returns: TypeName, + pub body: ScriptBody, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ScriptBody { + declare: Vec, + body: Vec, + exception_body: Option, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct VariableDeclare { + pub name: Identifier, + pub data_type: Option, + pub default: Expr, +} + +impl Display for VariableDeclare { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let VariableDeclare { + name, + data_type, + default, + } = self; + if let Some(data_type) = data_type { + write!(f, "{name} {data_type} := {default}")?; + } else { + write!(f, "{name} := {default}")?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct QueryDeclare { + pub name: Identifier, + pub query: Query, +} + +impl Display for QueryDeclare { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let QueryDeclare { name, query } = self; + write!(f, "{name} RESULTSET := {query}") + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum ScriptStatement { + LetVar { + span: Span, + declare: VariableDeclare, + }, + LetQuery { + span: Span, + declare: QueryDeclare, + }, + Assign { + span: Span, + name: Identifier, + value: Expr, + }, + Return { + span: Span, + value: Option, + }, + ForLoop { + span: Span, + variable: Identifier, + is_reverse: bool, + lower_bound: Expr, + upper_bound: Expr, + body: Vec, + label: Option, + }, + ForIn { + span: Span, + variable: Identifier, + resultset: Identifier, + body: Vec, + label: Option, + }, + WhileLoop { + span: Span, + condition: Expr, + body: Vec, + label: Option, + }, + RepeatLoop { + span: Span, + body: Vec, + until_condition: Expr, + label: Option, + }, + Loop { + span: Span, + body: Vec, + label: Option, + }, + Break { + span: Span, + label: Option, + }, + Continue { + span: Span, + label: Option, + }, + Case { + span: Span, + operand: Option, + conditions: Vec, + results: Vec>, + else_result: Option>, + }, + If { + span: Span, + conditions: Vec, + results: Vec>, + else_result: Option>, + }, + SQLStatement { + span: Span, + stmt: Statement, + }, +} + +impl Display for ScriptStatement { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ScriptStatement::LetVar { declare, .. } => write!(f, "LET {declare}"), + ScriptStatement::LetQuery { declare, .. } => write!(f, "LET {declare}"), + ScriptStatement::Assign { name, value, .. } => write!(f, "{name} := {value}"), + ScriptStatement::Return { value, .. } => { + if let Some(value) = value { + write!(f, "RETURN {value}") + } else { + write!(f, "RETURN") + } + } + ScriptStatement::ForLoop { + variable, + is_reverse, + lower_bound, + upper_bound, + body, + label, + .. + } => { + let reverse = if *is_reverse { " REVERSE" } else { "" }; + writeln!( + f, + "FOR {variable} IN{reverse} {lower_bound} TO {upper_bound} DO" + )?; + for stmt in body { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH, format!("{stmt};")) + )?; + } + write!(f, "END FOR")?; + if let Some(label) = label { + write!(f, " {label}")?; + } + Ok(()) + } + ScriptStatement::ForIn { + variable, + resultset, + body, + label, + .. + } => { + writeln!(f, "FOR {variable} IN {resultset} DO")?; + for stmt in body { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH, format!("{stmt};")) + )?; + } + write!(f, "END FOR")?; + if let Some(label) = label { + write!(f, " {label}")?; + } + Ok(()) + } + ScriptStatement::WhileLoop { + condition, + body, + label, + .. + } => { + writeln!(f, "WHILE {condition} DO")?; + for stmt in body { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH, format!("{stmt};")) + )?; + } + write!(f, "END WHILE")?; + if let Some(label) = label { + write!(f, " {label}")?; + } + Ok(()) + } + ScriptStatement::RepeatLoop { + until_condition, + body, + label, + .. + } => { + writeln!(f, "REPEAT")?; + for stmt in body { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH, format!("{stmt};")) + )?; + } + writeln!(f, "UNTIL {until_condition}")?; + write!(f, "END REPEAT")?; + if let Some(label) = label { + write!(f, " {label}")?; + } + Ok(()) + } + ScriptStatement::Loop { body, label, .. } => { + writeln!(f, "LOOP")?; + for stmt in body { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH, format!("{stmt};")) + )?; + } + write!(f, "END LOOP")?; + if let Some(label) = label { + write!(f, " {label}")?; + } + Ok(()) + } + ScriptStatement::Break { label, .. } => { + write!(f, "BREAK")?; + if let Some(label) = label { + write!(f, " {label}")?; + } + Ok(()) + } + ScriptStatement::Continue { label, .. } => { + write!(f, "CONTINUE")?; + if let Some(label) = label { + write!(f, " {label}")?; + } + Ok(()) + } + ScriptStatement::Case { + operand, + conditions, + results, + else_result, + .. + } => { + if let Some(operand) = operand { + writeln!(f, "CASE {operand}")?; + } else { + writeln!(f, "CASE")?; + } + for (condition, result) in conditions.iter().zip(results.iter()) { + writeln!(f, "{:INDENT_DEPTH$}WHEN {condition} THEN", " ")?; + for stmt in result { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH * 2, format!("{stmt};")) + )?; + } + } + if let Some(else_result) = else_result { + writeln!(f, "{:INDENT_DEPTH$}ELSE", " ")?; + for stmt in else_result { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH * 2, format!("{stmt};")) + )?; + } + } + write!(f, "END CASE") + } + ScriptStatement::If { + conditions, + results, + else_result, + .. + } => { + for (i, (condition, result)) in conditions.iter().zip(results.iter()).enumerate() { + if i == 0 { + writeln!(f, "IF {condition} THEN")?; + } else { + writeln!(f, "ELSEIF {condition} THEN")?; + } + for stmt in result { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH, format!("{stmt};")) + )?; + } + } + if let Some(else_result) = else_result { + writeln!(f, "ELSE")?; + for stmt in else_result { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH, format!("{stmt};")) + )?; + } + } + write!(f, "END IF") + } + ScriptStatement::SQLStatement { stmt, .. } => write!(f, "{stmt}"), + } + } +} diff --git a/src/query/ast/src/parser/common.rs b/src/query/ast/src/parser/common.rs index f3b49789c286a..5ef6c343198d2 100644 --- a/src/query/ast/src/parser/common.rs +++ b/src/query/ast/src/parser/common.rs @@ -258,7 +258,7 @@ pub fn comma_separated_list0_ignore_trailing<'a, T>( nom::multi::separated_list0(match_text(","), item) } -pub fn task_statements<'a, T>( +pub fn semicolon_terminated_list1<'a, T>( item: impl FnMut(Input<'a>) -> IResult<'a, T>, ) -> impl FnMut(Input<'a>) -> IResult<'a, Vec> { many1(terminated(item, match_text(";"))) diff --git a/src/query/ast/src/parser/mod.rs b/src/query/ast/src/parser/mod.rs index 2f2b877d69a00..5885099ab9658 100644 --- a/src/query/ast/src/parser/mod.rs +++ b/src/query/ast/src/parser/mod.rs @@ -17,6 +17,7 @@ mod data_mask; pub mod expr; pub mod query; pub mod quote; +pub mod script; mod share; mod stage; pub mod statement; diff --git a/src/query/ast/src/parser/script.rs b/src/query/ast/src/parser/script.rs new file mode 100644 index 0000000000000..a494fcb9496a3 --- /dev/null +++ b/src/query/ast/src/parser/script.rs @@ -0,0 +1,228 @@ +// Copyright 2021 Datafuse Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use nom::combinator::consumed; +use nom::combinator::map; + +use crate::ast::*; +use crate::parser::common::*; +use crate::parser::expr::*; +use crate::parser::input::Input; +use crate::parser::query::*; +use crate::parser::statement::*; +use crate::parser::token::*; +use crate::rule; + +pub fn script_stmt(i: Input) -> IResult { + let let_query_stmt = map( + consumed(rule! { + LET ~^#ident ~ RESULTSET ~ ^":=" ~ ^#query + }), + |(span, (_, name, _, _, query))| ScriptStatement::LetQuery { + span: transform_span(span.0), + declare: QueryDeclare { name, query }, + }, + ); + let let_var_stmt = map( + consumed(rule! { + LET ~^#ident ~ #type_name? ~ ^":=" ~ ^#expr + }), + |(span, (_, name, data_type, _, default))| ScriptStatement::LetVar { + span: transform_span(span.0), + declare: VariableDeclare { + name, + data_type, + default, + }, + }, + ); + let assign_stmt = map( + consumed(rule! { + #ident ~ ":=" ~ ^#expr + }), + |(span, (name, _, value))| ScriptStatement::Assign { + span: transform_span(span.0), + name, + value, + }, + ); + let return_stmt = map( + consumed(rule! { + RETURN ~ #expr? + }), + |(span, (_, value))| ScriptStatement::Return { + span: transform_span(span.0), + value, + }, + ); + let for_loop_stmt = map( + consumed(rule! { + FOR ~ ^#ident ~ ^IN ~ REVERSE? + ~ #expr ~ TO ~ #expr ~ ^DO + ~ ^#semicolon_terminated_list1(script_stmt) + ~ ^END ~ ^FOR ~ #ident? + }), + |( + span, + (_, variable, _, is_reverse, lower_bound, _, upper_bound, _, body, _, _, label), + )| ScriptStatement::ForLoop { + span: transform_span(span.0), + variable, + is_reverse: is_reverse.is_some(), + lower_bound, + upper_bound, + body, + label, + }, + ); + let for_in_stmt = map( + consumed(rule! { + FOR ~ ^#ident ~ ^IN ~ #ident ~ ^DO + ~ ^#semicolon_terminated_list1(script_stmt) + ~ ^END ~ ^FOR ~ #ident? + }), + |(span, (_, variable, _, resultset, _, body, _, _, label))| ScriptStatement::ForIn { + span: transform_span(span.0), + variable, + resultset, + body, + label, + }, + ); + let while_loop_stmt = map( + consumed(rule! { + WHILE ~ ^#expr ~ ^DO + ~ ^#semicolon_terminated_list1(script_stmt) + ~ ^END ~ ^WHILE ~ #ident? + }), + |(span, (_, condition, _, body, _, _, label))| ScriptStatement::WhileLoop { + span: transform_span(span.0), + condition, + body, + label, + }, + ); + let repeat_loop_stmt = map( + consumed(rule! { + REPEAT + ~ ^#semicolon_terminated_list1(script_stmt) + ~ ^UNTIL ~ ^#expr + ~ ^END ~ ^REPEAT ~ #ident? + }), + |(span, (_, body, _, until_condition, _, _, label))| ScriptStatement::RepeatLoop { + span: transform_span(span.0), + body, + until_condition, + label, + }, + ); + let loop_stmt = map( + consumed(rule! { + LOOP ~ ^#semicolon_terminated_list1(script_stmt) ~ ^END ~ ^LOOP ~ #ident? + }), + |(span, (_, body, _, _, label))| ScriptStatement::Loop { + span: transform_span(span.0), + body, + label, + }, + ); + let break_stmt = map( + consumed(rule! { + BREAK ~ #ident? + }), + |(span, (_, label))| ScriptStatement::Break { + span: transform_span(span.0), + label, + }, + ); + let continue_stmt = map( + consumed(rule! { + CONTINUE ~ #ident? + }), + |(span, (_, label))| ScriptStatement::Continue { + span: transform_span(span.0), + label, + }, + ); + let case_stmt = map( + consumed(rule! { + CASE ~ #expr? + ~ ( WHEN ~ ^#expr ~ ^THEN ~ ^#semicolon_terminated_list1(script_stmt) )+ + ~ ( ELSE ~ ^#semicolon_terminated_list1(script_stmt) )? + ~ ^END ~ CASE? + }), + |(span, (_, operand, branches, else_result, _, _))| { + let (conditions, results) = branches + .into_iter() + .map(|(_, cond, _, result)| (cond, result)) + .unzip(); + let else_result = else_result.map(|(_, result)| result); + ScriptStatement::Case { + span: transform_span(span.0), + operand, + conditions, + results, + else_result, + } + }, + ); + let if_stmt = map( + consumed(rule! { + IF ~ ^#expr ~ ^THEN ~ ^#semicolon_terminated_list1(script_stmt) + ~ ( ELSEIF ~ ^#expr ~ ^THEN ~ ^#semicolon_terminated_list1(script_stmt) )* + ~ ( ELSE ~ ^#semicolon_terminated_list1(script_stmt) )? + ~ ^END ~ ^IF + }), + |(span, (_, condition, _, result, else_ifs, else_result, _, _))| { + let (mut conditions, mut results) = (vec![condition], vec![result]); + for (_, cond, _, result) in else_ifs { + conditions.push(cond); + results.push(result); + } + let else_result = else_result.map(|(_, result)| result); + ScriptStatement::If { + span: transform_span(span.0), + conditions, + results, + else_result, + } + }, + ); + let sql_stmt = map( + consumed(rule! { + #statement_body + }), + |(span, stmt)| ScriptStatement::SQLStatement { + span: transform_span(span.0), + stmt, + }, + ); + + rule!( + #let_query_stmt + | #let_var_stmt + | #assign_stmt + | #return_stmt + | #for_loop_stmt + | #for_in_stmt + | #while_loop_stmt + | #repeat_loop_stmt + | #loop_stmt + | #break_stmt + | #continue_stmt + | #case_stmt + | #if_stmt + | #sql_stmt + )(i) +} diff --git a/src/query/ast/src/parser/statement.rs b/src/query/ast/src/parser/statement.rs index 0baca37b690cd..b0bad53643613 100644 --- a/src/query/ast/src/parser/statement.rs +++ b/src/query/ast/src/parser/statement.rs @@ -3276,7 +3276,7 @@ pub fn task_sql_block(i: Input) -> IResult { let task_block = map( rule! { BEGIN - ~ #task_statements(statement_body) + ~ #semicolon_terminated_list1(statement_body) ~ END }, |(_, stmts, _)| { diff --git a/src/query/ast/src/parser/token.rs b/src/query/ast/src/parser/token.rs index 28abff7c8b4dc..745781141dc1a 100644 --- a/src/query/ast/src/parser/token.rs +++ b/src/query/ast/src/parser/token.rs @@ -219,6 +219,8 @@ pub enum TokenKind { Colon, #[token("::")] DoubleColon, + #[token(":=")] + ColonEqual, #[token(";")] SemiColon, #[token("\\")] @@ -318,6 +320,8 @@ pub enum TokenKind { // 2. Search in this file to see if the new keyword is a commented // out reserved keyword. If so, uncomment the keyword in the // reserved list. + #[token("ACCOUNT", ignore(ascii_case))] + ACCOUNT, #[token("ALL", ignore(ascii_case))] ALL, #[token("ALLOWED_IP_LIST", ignore(ascii_case))] @@ -370,6 +374,8 @@ pub enum TokenKind { BIGINT, #[token("BINARY", ignore(ascii_case))] BINARY, + #[token("BREAK", ignore(ascii_case))] + BREAK, #[token("LONGBLOB", ignore(ascii_case))] LONGBLOB, #[token("MEDIUMBLOB", ignore(ascii_case))] @@ -422,6 +428,8 @@ pub enum TokenKind { CONNECTIONS, #[token("CONTENT_TYPE", ignore(ascii_case))] CONTENT_TYPE, + #[token("CONTINUE", ignore(ascii_case))] + CONTINUE, #[token("CHAR", ignore(ascii_case))] CHAR, #[token("COLUMN", ignore(ascii_case))] @@ -496,6 +504,8 @@ pub enum TokenKind { DIV, #[token("DOUBLE_SHA1_PASSWORD", ignore(ascii_case))] DOUBLE_SHA1_PASSWORD, + #[token("DO", ignore(ascii_case))] + DO, #[token("DOUBLE", ignore(ascii_case))] DOUBLE, #[token("DOW", ignore(ascii_case))] @@ -544,6 +554,8 @@ pub enum TokenKind { EXPIRE, #[token("EXTRACT", ignore(ascii_case))] EXTRACT, + #[token("ELSEIF", ignore(ascii_case))] + ELSEIF, #[token("FALSE", ignore(ascii_case))] FALSE, #[token("FIELDS", ignore(ascii_case))] @@ -680,8 +692,8 @@ pub enum TokenKind { LOCKS, #[token("LOGICAL", ignore(ascii_case))] LOGICAL, - #[token("ACCOUNT", ignore(ascii_case))] - ACCOUNT, + #[token("LOOP", ignore(ascii_case))] + LOOP, #[token("SECONDARY", ignore(ascii_case))] SECONDARY, #[token("ROLES", ignore(ascii_case))] @@ -693,6 +705,8 @@ pub enum TokenKind { LEADING, #[token("LEFT", ignore(ascii_case))] LEFT, + #[token("LET", ignore(ascii_case))] + LET, #[token("LIKE", ignore(ascii_case))] LIKE, #[token("LIMIT", ignore(ascii_case))] @@ -847,6 +861,8 @@ pub enum TokenKind { REPLACE, #[token("RETURN_FAILED_ONLY", ignore(ascii_case))] RETURN_FAILED_ONLY, + #[token("REVERSE", ignore(ascii_case))] + REVERSE, #[token("MERGE", ignore(ascii_case))] MERGE, #[token("MATCHED", ignore(ascii_case))] @@ -865,6 +881,8 @@ pub enum TokenKind { ROW_TAG, #[token("GRANT", ignore(ascii_case))] GRANT, + #[token("REPEAT", ignore(ascii_case))] + REPEAT, #[token("ROLE", ignore(ascii_case))] ROLE, #[token("PRECEDING", ignore(ascii_case))] @@ -889,6 +907,8 @@ pub enum TokenKind { RETURN, #[token("RETURNS", ignore(ascii_case))] RETURNS, + #[token("RESULTSET", ignore(ascii_case))] + RESULTSET, #[token("RUN", ignore(ascii_case))] RUN, #[token("GRANTS", ignore(ascii_case))] @@ -1093,6 +1113,8 @@ pub enum TokenKind { WHEN, #[token("WHERE", ignore(ascii_case))] WHERE, + #[token("WHILE", ignore(ascii_case))] + WHILE, #[token("WINDOW", ignore(ascii_case))] WINDOW, #[token("WITH", ignore(ascii_case))] @@ -1185,6 +1207,8 @@ pub enum TokenKind { PREFIX, #[token("MODIFIED_AFTER", ignore(ascii_case))] MODIFIED_AFTER, + #[token("UNTIL", ignore(ascii_case))] + UNTIL, #[token("BEGIN", ignore(ascii_case))] BEGIN, #[token("COMMIT", ignore(ascii_case))] @@ -1248,6 +1272,7 @@ impl TokenKind { | Dot | Colon | DoubleColon + | ColonEqual | SemiColon | Backslash | LBracket @@ -1278,6 +1303,11 @@ impl TokenKind { | Placeholder | QuestionOr | QuestionAnd + | ArrowAt + | AtArrow + | AtQuestion + | AtAt + | HashMinus | EOI ) } @@ -1316,7 +1346,7 @@ impl TokenKind { // | TokenKind::DEFERRABLE | TokenKind::DESC | TokenKind::DISTINCT - // | TokenKind::DO + | TokenKind::DO | TokenKind::ELSE | TokenKind::END | TokenKind::EXISTS diff --git a/src/query/ast/tests/it/parser.rs b/src/query/ast/tests/it/parser.rs index 333cc44eb1da1..98350451d2e69 100644 --- a/src/query/ast/tests/it/parser.rs +++ b/src/query/ast/tests/it/parser.rs @@ -22,6 +22,7 @@ use databend_common_ast::parser::parse_sql; use databend_common_ast::parser::query::*; use databend_common_ast::parser::quote::quote_ident; use databend_common_ast::parser::quote::unquote_ident; +use databend_common_ast::parser::script::script_stmt; use databend_common_ast::parser::statement::insert_stmt; use databend_common_ast::parser::token::*; use databend_common_ast::parser::tokenize_sql; @@ -45,6 +46,8 @@ where P: FnMut(Input) -> IResult, O: Debug + Display, { + let src = unindent::unindent(src); + let src = src.trim(); let tokens = tokenize_sql(src).unwrap(); let backtrace = Backtrace::new(); let parser = parser; @@ -194,7 +197,7 @@ fn test_statement() { r#"select 'stringwith"doublequote'"#, r#"select '🦈'"#, r#"insert into t (c1, c2) values (1, 2), (3, 4);"#, - r#"insert into t (c1, c2) values (1, 2); "#, + r#"insert into t (c1, c2) values (1, 2);"#, r#"insert into table t select * from t2;"#, r#"select parse_json('{"k1": [0, 1, 2]}').k1[0];"#, r#"CREATE STAGE ~"#, @@ -747,7 +750,7 @@ fn test_raw_insert_stmt() { let file = &mut mint.new_goldenfile("raw_insert.txt").unwrap(); let cases = &[ r#"insert into t (c1, c2) values (1, 2), (3, 4);"#, - r#"insert into t (c1, c2) values (1, 2); "#, + r#"insert into t (c1, c2) values (1, 2);"#, r#"insert into table t format json;"#, r#"insert into table t select * from t2;"#, ]; @@ -923,7 +926,7 @@ fn test_expr() { r#"SUM(salary) OVER ()"#, r#"AVG(salary) OVER (PARTITION BY department)"#, r#"SUM(salary) OVER (PARTITION BY department ORDER BY salary DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)"#, - r#"AVG(salary) OVER (PARTITION BY department ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) "#, + r#"AVG(salary) OVER (PARTITION BY department ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)"#, r#"COUNT() OVER (ORDER BY hire_date RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW)"#, r#"COUNT() OVER (ORDER BY hire_date ROWS UNBOUNDED PRECEDING)"#, r#"COUNT() OVER (ORDER BY hire_date ROWS CURRENT ROW)"#, @@ -973,9 +976,11 @@ fn test_expr_error() { r#"CAST(col1)"#, r#"a.add(b)"#, r#"[ x * 100 FOR x in [1,2,3] if x % 2 = 0 ]"#, - r#"G.E.B IS NOT NULL AND - col1 NOT BETWEEN col2 AND - AND 1 + col3 DIV sum(col4)"#, + r#" + G.E.B IS NOT NULL AND + col1 NOT BETWEEN col2 AND + AND 1 + col3 DIV sum(col4) + "#, ]; for case in cases { @@ -983,6 +988,84 @@ fn test_expr_error() { } } +#[test] +fn test_script() { + let mut mint = Mint::new("tests/it/testdata"); + let file = &mut mint.new_goldenfile("script.txt").unwrap(); + + let cases = &[ + r#"LET cost DECIMAL(38, 2) := 100.0"#, + r#"LET t1 RESULTSET := SELECT * FROM numbers(100)"#, + r#"profit := revenue - cost"#, + r#"RETURN profit"#, + r#"RETURN"#, + r#" + FOR i IN REVERSE 1 TO maximum_count DO + counter := counter + 1; + END FOR label1 + "#, + r#" + FOR rec IN resultset DO + CONTINUE; + END FOR label1 + "#, + r#" + WHILE counter < maximum_count DO + CONTINUE label1; + END WHILE label1 + "#, + r#" + REPEAT + BREAK; + UNTIL counter = maximum_count + END REPEAT label1 + "#, + r#" + LOOP + BREAK label1; + END LOOP label1 + "#, + r#" + CASE + WHEN counter = 1 THEN + counter := counter + 1; + WHEN counter = 2 THEN + counter := counter + 2; + ELSE + counter := counter + 3; + END + "#, + r#" + CASE counter + WHEN 1 THEN + counter := counter + 1; + WHEN 2 THEN + counter := counter + 2; + ELSE + counter := counter + 3; + END CASE + "#, + r#" + IF counter = 1 THEN + counter := counter + 1; + ELSEIF counter = 2 THEN + counter := counter + 2; + ELSE + counter := counter + 3; + END IF + "#, + r#" + LOOP + SELECT c1, c2 FROM t WHERE c1 = 1; + END LOOP + "#, + ]; + + for case in cases { + run_parser(file, script_stmt, case); + } +} + #[test] fn test_quote() { let cases = &[ diff --git a/src/query/ast/tests/it/testdata/expr-error.txt b/src/query/ast/tests/it/testdata/expr-error.txt index 8c7e3424a1d15..859bc158ebe67 100644 --- a/src/query/ast/tests/it/testdata/expr-error.txt +++ b/src/query/ast/tests/it/testdata/expr-error.txt @@ -89,17 +89,17 @@ error: ---------- Input ---------- G.E.B IS NOT NULL AND - col1 NOT BETWEEN col2 AND - AND 1 + col3 DIV sum(col4) + col1 NOT BETWEEN col2 AND + AND 1 + col3 DIV sum(col4) ---------- Output --------- error: - --> SQL:3:17 + --> SQL:3:9 | 1 | G.E.B IS NOT NULL AND | - while parsing expression -2 | col1 NOT BETWEEN col2 AND - | --- while parsing `[NOT] BETWEEN ... AND ...` -3 | AND 1 + col3 DIV sum(col4) - | ^^^ expected more tokens for expression +2 | col1 NOT BETWEEN col2 AND + | --- while parsing `[NOT] BETWEEN ... AND ...` +3 | AND 1 + col3 DIV sum(col4) + | ^^^ expected more tokens for expression diff --git a/src/query/ast/tests/it/testdata/expr.txt b/src/query/ast/tests/it/testdata/expr.txt index 7cb65f90ad668..d06c1789eadda 100644 --- a/src/query/ast/tests/it/testdata/expr.txt +++ b/src/query/ast/tests/it/testdata/expr.txt @@ -2512,48 +2512,48 @@ BinaryOp { ---------- Input ---------- p_partkey = l_partkey - AND p_brand = 'Brand#12' - AND p_container IN ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') - AND l_quantity >= CAST (1 AS smallint) AND l_quantity <= CAST (1 + 10 AS smallint) - AND p_size BETWEEN CAST (1 AS smallint) AND CAST (5 AS smallint) - AND l_shipmode IN ('AIR', 'AIR REG') - AND l_shipinstruct = 'DELIVER IN PERSON' +AND p_brand = 'Brand#12' +AND p_container IN ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') +AND l_quantity >= CAST (1 AS smallint) AND l_quantity <= CAST (1 + 10 AS smallint) +AND p_size BETWEEN CAST (1 AS smallint) AND CAST (5 AS smallint) +AND l_shipmode IN ('AIR', 'AIR REG') +AND l_shipinstruct = 'DELIVER IN PERSON' ---------- Output --------- ((((((((p_partkey = l_partkey) AND (p_brand = 'Brand#12')) AND p_container IN('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG')) AND (l_quantity >= CAST(1 AS Int16))) AND (l_quantity <= CAST((1 + 10) AS Int16))) AND p_size BETWEEN CAST(1 AS Int16) AND CAST(5 AS Int16)) AND l_shipmode IN('AIR', 'AIR REG')) AND (l_shipinstruct = 'DELIVER IN PERSON')) ---------- AST ------------ BinaryOp { span: Some( - 366..369, + 294..297, ), op: And, left: BinaryOp { span: Some( - 317..320, + 257..260, ), op: And, left: BinaryOp { span: Some( - 240..243, + 192..195, ), op: And, left: BinaryOp { span: Some( - 184..187, + 148..151, ), op: And, left: BinaryOp { span: Some( - 145..148, + 109..112, ), op: And, left: BinaryOp { span: Some( - 71..74, + 47..50, ), op: And, left: BinaryOp { span: Some( - 34..37, + 22..25, ), op: And, left: BinaryOp { @@ -2600,12 +2600,12 @@ BinaryOp { }, right: BinaryOp { span: Some( - 46..47, + 34..35, ), op: Eq, left: ColumnRef { span: Some( - 38..45, + 26..33, ), column: ColumnRef { database: None, @@ -2613,7 +2613,7 @@ BinaryOp { column: Name( Identifier { span: Some( - 38..45, + 26..33, ), name: "p_brand", quote: None, @@ -2623,7 +2623,7 @@ BinaryOp { }, right: Literal { span: Some( - 48..58, + 36..46, ), lit: String( "Brand#12", @@ -2633,11 +2633,11 @@ BinaryOp { }, right: InList { span: Some( - 87..132, + 63..108, ), expr: ColumnRef { span: Some( - 75..86, + 51..62, ), column: ColumnRef { database: None, @@ -2645,7 +2645,7 @@ BinaryOp { column: Name( Identifier { span: Some( - 75..86, + 51..62, ), name: "p_container", quote: None, @@ -2656,7 +2656,7 @@ BinaryOp { list: [ Literal { span: Some( - 91..100, + 67..76, ), lit: String( "SM CASE", @@ -2664,7 +2664,7 @@ BinaryOp { }, Literal { span: Some( - 102..110, + 78..86, ), lit: String( "SM BOX", @@ -2672,7 +2672,7 @@ BinaryOp { }, Literal { span: Some( - 112..121, + 88..97, ), lit: String( "SM PACK", @@ -2680,7 +2680,7 @@ BinaryOp { }, Literal { span: Some( - 123..131, + 99..107, ), lit: String( "SM PKG", @@ -2692,12 +2692,12 @@ BinaryOp { }, right: BinaryOp { span: Some( - 160..162, + 124..126, ), op: Gte, left: ColumnRef { span: Some( - 149..159, + 113..123, ), column: ColumnRef { database: None, @@ -2705,7 +2705,7 @@ BinaryOp { column: Name( Identifier { span: Some( - 149..159, + 113..123, ), name: "l_quantity", quote: None, @@ -2715,11 +2715,11 @@ BinaryOp { }, right: Cast { span: Some( - 163..183, + 127..147, ), expr: Literal { span: Some( - 169..170, + 133..134, ), lit: UInt64( 1, @@ -2732,12 +2732,12 @@ BinaryOp { }, right: BinaryOp { span: Some( - 199..201, + 163..165, ), op: Lte, left: ColumnRef { span: Some( - 188..198, + 152..162, ), column: ColumnRef { database: None, @@ -2745,7 +2745,7 @@ BinaryOp { column: Name( Identifier { span: Some( - 188..198, + 152..162, ), name: "l_quantity", quote: None, @@ -2755,16 +2755,16 @@ BinaryOp { }, right: Cast { span: Some( - 202..227, + 166..191, ), expr: BinaryOp { span: Some( - 210..211, + 174..175, ), op: Plus, left: Literal { span: Some( - 208..209, + 172..173, ), lit: UInt64( 1, @@ -2772,7 +2772,7 @@ BinaryOp { }, right: Literal { span: Some( - 212..214, + 176..178, ), lit: UInt64( 10, @@ -2786,11 +2786,11 @@ BinaryOp { }, right: Between { span: Some( - 251..304, + 203..256, ), expr: ColumnRef { span: Some( - 244..250, + 196..202, ), column: ColumnRef { database: None, @@ -2798,7 +2798,7 @@ BinaryOp { column: Name( Identifier { span: Some( - 244..250, + 196..202, ), name: "p_size", quote: None, @@ -2808,11 +2808,11 @@ BinaryOp { }, low: Cast { span: Some( - 259..279, + 211..231, ), expr: Literal { span: Some( - 265..266, + 217..218, ), lit: UInt64( 1, @@ -2823,11 +2823,11 @@ BinaryOp { }, high: Cast { span: Some( - 284..304, + 236..256, ), expr: Literal { span: Some( - 290..291, + 242..243, ), lit: UInt64( 5, @@ -2841,11 +2841,11 @@ BinaryOp { }, right: InList { span: Some( - 332..353, + 272..293, ), expr: ColumnRef { span: Some( - 321..331, + 261..271, ), column: ColumnRef { database: None, @@ -2853,7 +2853,7 @@ BinaryOp { column: Name( Identifier { span: Some( - 321..331, + 261..271, ), name: "l_shipmode", quote: None, @@ -2864,7 +2864,7 @@ BinaryOp { list: [ Literal { span: Some( - 336..341, + 276..281, ), lit: String( "AIR", @@ -2872,7 +2872,7 @@ BinaryOp { }, Literal { span: Some( - 343..352, + 283..292, ), lit: String( "AIR REG", @@ -2884,12 +2884,12 @@ BinaryOp { }, right: BinaryOp { span: Some( - 385..386, + 313..314, ), op: Eq, left: ColumnRef { span: Some( - 370..384, + 298..312, ), column: ColumnRef { database: None, @@ -2897,7 +2897,7 @@ BinaryOp { column: Name( Identifier { span: Some( - 370..384, + 298..312, ), name: "l_shipinstruct", quote: None, @@ -2907,7 +2907,7 @@ BinaryOp { }, right: Literal { span: Some( - 387..406, + 315..334, ), lit: String( "DELIVER IN PERSON", @@ -3700,7 +3700,7 @@ FunctionCall { ---------- Input ---------- -AVG(salary) OVER (PARTITION BY department ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) +AVG(salary) OVER (PARTITION BY department ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) ---------- Output --------- AVG(salary) OVER (PARTITION BY department ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) ---------- AST ------------ diff --git a/src/query/ast/tests/it/testdata/query.txt b/src/query/ast/tests/it/testdata/query.txt index de282cfc73634..3470ab723e107 100644 --- a/src/query/ast/tests/it/testdata/query.txt +++ b/src/query/ast/tests/it/testdata/query.txt @@ -2867,33 +2867,33 @@ Query { ---------- Input ---------- select c_count cc, count(*) as custdist, sum(c_acctbal) as totacctbal - from customer, orders ODS, - ( - select - c_custkey, - count(o_orderkey) - from - customer left outer join orders on - c_custkey = o_custkey - and o_comment not like '%:1%:2%' - group by - c_custkey - ) as c_orders - group by c_count - order by custdist desc nulls first, c_count asc, totacctbal nulls last - limit 10, totacctbal +from customer, orders ODS, + ( + select + c_custkey, + count(o_orderkey) + from + customer left outer join orders on + c_custkey = o_custkey + and o_comment not like '%:1%:2%' + group by + c_custkey + ) as c_orders +group by c_count +order by custdist desc nulls first, c_count asc, totacctbal nulls last +limit 10, totacctbal ---------- Output --------- SELECT c_count AS cc, COUNT(*) AS custdist, sum(c_acctbal) AS totacctbal FROM customer, orders AS ODS, (SELECT c_custkey, count(o_orderkey) FROM customer LEFT OUTER JOIN orders ON ((c_custkey = o_custkey) AND (o_comment NOT LIKE '%:1%:2%')) GROUP BY c_custkey) AS c_orders GROUP BY c_count ORDER BY custdist DESC NULLS FIRST, c_count ASC, totacctbal NULLS LAST LIMIT 10, totacctbal ---------- AST ------------ Query { span: Some( - 0..547, + 0..391, ), with: None, body: Select( SelectStmt { span: Some( - 0..547, + 0..391, ), hints: None, distinct: false, @@ -2997,13 +2997,13 @@ Query { from: [ Table { span: Some( - 87..95, + 75..83, ), catalog: None, database: None, table: Identifier { span: Some( - 87..95, + 75..83, ), name: "customer", quote: None, @@ -3016,13 +3016,13 @@ Query { }, Table { span: Some( - 97..107, + 85..95, ), catalog: None, database: None, table: Identifier { span: Some( - 97..103, + 85..91, ), name: "orders", quote: None, @@ -3031,7 +3031,7 @@ Query { TableAlias { name: Identifier { span: Some( - 104..107, + 92..95, ), name: "ODS", quote: None, @@ -3046,18 +3046,18 @@ Query { }, Subquery { span: Some( - 125..518, + 101..374, ), lateral: false, subquery: Query { span: Some( - 147..488, + 111..356, ), with: None, body: Select( SelectStmt { span: Some( - 147..488, + 111..356, ), hints: None, distinct: false, @@ -3065,7 +3065,7 @@ Query { AliasedExpr { expr: ColumnRef { span: Some( - 178..187, + 130..139, ), column: ColumnRef { database: None, @@ -3073,7 +3073,7 @@ Query { column: Name( Identifier { span: Some( - 178..187, + 130..139, ), name: "c_custkey", quote: None, @@ -3086,13 +3086,13 @@ Query { AliasedExpr { expr: FunctionCall { span: Some( - 213..230, + 153..170, ), func: FunctionCall { distinct: false, name: Identifier { span: Some( - 213..218, + 153..158, ), name: "count", quote: None, @@ -3100,7 +3100,7 @@ Query { args: [ ColumnRef { span: Some( - 219..229, + 159..169, ), column: ColumnRef { database: None, @@ -3108,7 +3108,7 @@ Query { column: Name( Identifier { span: Some( - 219..229, + 159..169, ), name: "o_orderkey", quote: None, @@ -3128,24 +3128,24 @@ Query { from: [ Join { span: Some( - 289..304, + 205..220, ), join: Join { op: LeftOuter, condition: On( BinaryOp { span: Some( - 393..396, + 285..288, ), op: And, left: BinaryOp { span: Some( - 353..354, + 257..258, ), op: Eq, left: ColumnRef { span: Some( - 343..352, + 247..256, ), column: ColumnRef { database: None, @@ -3153,7 +3153,7 @@ Query { column: Name( Identifier { span: Some( - 343..352, + 247..256, ), name: "c_custkey", quote: None, @@ -3163,7 +3163,7 @@ Query { }, right: ColumnRef { span: Some( - 355..364, + 259..268, ), column: ColumnRef { database: None, @@ -3171,7 +3171,7 @@ Query { column: Name( Identifier { span: Some( - 355..364, + 259..268, ), name: "o_custkey", quote: None, @@ -3182,12 +3182,12 @@ Query { }, right: BinaryOp { span: Some( - 407..415, + 299..307, ), op: NotLike, left: ColumnRef { span: Some( - 397..406, + 289..298, ), column: ColumnRef { database: None, @@ -3195,7 +3195,7 @@ Query { column: Name( Identifier { span: Some( - 397..406, + 289..298, ), name: "o_comment", quote: None, @@ -3205,7 +3205,7 @@ Query { }, right: Literal { span: Some( - 416..425, + 308..317, ), lit: String( "%:1%:2%", @@ -3216,13 +3216,13 @@ Query { ), left: Table { span: Some( - 280..288, + 196..204, ), catalog: None, database: None, table: Identifier { span: Some( - 280..288, + 196..204, ), name: "customer", quote: None, @@ -3235,13 +3235,13 @@ Query { }, right: Table { span: Some( - 305..311, + 221..227, ), catalog: None, database: None, table: Identifier { span: Some( - 305..311, + 221..227, ), name: "orders", quote: None, @@ -3261,7 +3261,7 @@ Query { [ ColumnRef { span: Some( - 479..488, + 347..356, ), column: ColumnRef { database: None, @@ -3269,7 +3269,7 @@ Query { column: Name( Identifier { span: Some( - 479..488, + 347..356, ), name: "c_custkey", quote: None, @@ -3294,7 +3294,7 @@ Query { TableAlias { name: Identifier { span: Some( - 510..518, + 366..374, ), name: "c_orders", quote: None, @@ -3310,7 +3310,7 @@ Query { [ ColumnRef { span: Some( - 540..547, + 384..391, ), column: ColumnRef { database: None, @@ -3318,7 +3318,7 @@ Query { column: Name( Identifier { span: Some( - 540..547, + 384..391, ), name: "c_count", quote: None, @@ -3338,7 +3338,7 @@ Query { OrderByExpr { expr: ColumnRef { span: Some( - 569..577, + 401..409, ), column: ColumnRef { database: None, @@ -3346,7 +3346,7 @@ Query { column: Name( Identifier { span: Some( - 569..577, + 401..409, ), name: "custdist", quote: None, @@ -3364,7 +3364,7 @@ Query { OrderByExpr { expr: ColumnRef { span: Some( - 596..603, + 428..435, ), column: ColumnRef { database: None, @@ -3372,7 +3372,7 @@ Query { column: Name( Identifier { span: Some( - 596..603, + 428..435, ), name: "c_count", quote: None, @@ -3388,7 +3388,7 @@ Query { OrderByExpr { expr: ColumnRef { span: Some( - 609..619, + 441..451, ), column: ColumnRef { database: None, @@ -3396,7 +3396,7 @@ Query { column: Name( Identifier { span: Some( - 609..619, + 441..451, ), name: "totacctbal", quote: None, @@ -3413,7 +3413,7 @@ Query { limit: [ Literal { span: Some( - 649..651, + 469..471, ), lit: UInt64( 10, @@ -3421,7 +3421,7 @@ Query { }, ColumnRef { span: Some( - 653..663, + 473..483, ), column: ColumnRef { database: None, @@ -3429,7 +3429,7 @@ Query { column: Name( Identifier { span: Some( - 653..663, + 473..483, ), name: "totacctbal", quote: None, diff --git a/src/query/ast/tests/it/testdata/raw_insert.txt b/src/query/ast/tests/it/testdata/raw_insert.txt index 45e5946b8173d..cc6c398523d71 100644 --- a/src/query/ast/tests/it/testdata/raw_insert.txt +++ b/src/query/ast/tests/it/testdata/raw_insert.txt @@ -41,9 +41,9 @@ Insert( ---------- Input ---------- -insert into t (c1, c2) values (1, 2); +insert into t (c1, c2) values (1, 2); ---------- Output --------- -INSERT INTO t (c1, c2) VALUES (1, 2); +INSERT INTO t (c1, c2) VALUES (1, 2); ---------- AST ------------ Insert( InsertStmt { @@ -74,7 +74,7 @@ Insert( }, ], source: RawValues { - rest_str: "(1, 2); ", + rest_str: "(1, 2);", start: 30, }, overwrite: false, diff --git a/src/query/ast/tests/it/testdata/script.txt b/src/query/ast/tests/it/testdata/script.txt new file mode 100644 index 0000000000000..4d1cb7a463103 --- /dev/null +++ b/src/query/ast/tests/it/testdata/script.txt @@ -0,0 +1,1401 @@ +---------- Input ---------- +LET cost DECIMAL(38, 2) := 100.0 +---------- Output --------- +LET cost Decimal(38, 2) := 100.0 +---------- AST ------------ +LetVar { + span: Some( + 0..32, + ), + declare: VariableDeclare { + name: Identifier { + span: Some( + 4..8, + ), + name: "cost", + quote: None, + }, + data_type: Some( + Decimal { + precision: 38, + scale: 2, + }, + ), + default: Literal { + span: Some( + 27..32, + ), + lit: Decimal256 { + value: 1000, + precision: 76, + scale: 1, + }, + }, + }, +} + + +---------- Input ---------- +LET t1 RESULTSET := SELECT * FROM numbers(100) +---------- Output --------- +LET t1 RESULTSET := SELECT * FROM numbers(100) +---------- AST ------------ +LetQuery { + span: Some( + 0..46, + ), + declare: QueryDeclare { + name: Identifier { + span: Some( + 4..6, + ), + name: "t1", + quote: None, + }, + query: Query { + span: Some( + 20..46, + ), + with: None, + body: Select( + SelectStmt { + span: Some( + 20..46, + ), + hints: None, + distinct: false, + select_list: [ + StarColumns { + qualified: [ + Star( + Some( + 27..28, + ), + ), + ], + column_filter: None, + }, + ], + from: [ + TableFunction { + span: Some( + 34..46, + ), + lateral: false, + name: Identifier { + span: Some( + 34..41, + ), + name: "numbers", + quote: None, + }, + params: [ + Literal { + span: Some( + 42..45, + ), + lit: UInt64( + 100, + ), + }, + ], + named_params: [], + alias: None, + }, + ], + selection: None, + group_by: None, + having: None, + window_list: None, + qualify: None, + }, + ), + order_by: [], + limit: [], + offset: None, + ignore_result: false, + }, + }, +} + + +---------- Input ---------- +profit := revenue - cost +---------- Output --------- +profit := (revenue - cost) +---------- AST ------------ +Assign { + span: Some( + 0..24, + ), + name: Identifier { + span: Some( + 0..6, + ), + name: "profit", + quote: None, + }, + value: BinaryOp { + span: Some( + 18..19, + ), + op: Minus, + left: ColumnRef { + span: Some( + 10..17, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 10..17, + ), + name: "revenue", + quote: None, + }, + ), + }, + }, + right: ColumnRef { + span: Some( + 20..24, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 20..24, + ), + name: "cost", + quote: None, + }, + ), + }, + }, + }, +} + + +---------- Input ---------- +RETURN profit +---------- Output --------- +RETURN profit +---------- AST ------------ +Return { + span: Some( + 0..13, + ), + value: Some( + ColumnRef { + span: Some( + 7..13, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 7..13, + ), + name: "profit", + quote: None, + }, + ), + }, + }, + ), +} + + +---------- Input ---------- +RETURN +---------- Output --------- +RETURN +---------- AST ------------ +Return { + span: Some( + 0..6, + ), + value: None, +} + + +---------- Input ---------- +FOR i IN REVERSE 1 TO maximum_count DO + counter := counter + 1; +END FOR label1 +---------- Output --------- +FOR i IN REVERSE 1 TO maximum_count DO + counter := (counter + 1); +END FOR label1 +---------- AST ------------ +ForLoop { + span: Some( + 0..81, + ), + variable: Identifier { + span: Some( + 4..5, + ), + name: "i", + quote: None, + }, + is_reverse: true, + lower_bound: Literal { + span: Some( + 17..18, + ), + lit: UInt64( + 1, + ), + }, + upper_bound: ColumnRef { + span: Some( + 22..35, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 22..35, + ), + name: "maximum_count", + quote: None, + }, + ), + }, + }, + body: [ + Assign { + span: Some( + 43..65, + ), + name: Identifier { + span: Some( + 43..50, + ), + name: "counter", + quote: None, + }, + value: BinaryOp { + span: Some( + 62..63, + ), + op: Plus, + left: ColumnRef { + span: Some( + 54..61, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 54..61, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 64..65, + ), + lit: UInt64( + 1, + ), + }, + }, + }, + ], + label: Some( + Identifier { + span: Some( + 75..81, + ), + name: "label1", + quote: None, + }, + ), +} + + +---------- Input ---------- +FOR rec IN resultset DO + CONTINUE; +END FOR label1 +---------- Output --------- +FOR rec IN resultset DO + CONTINUE; +END FOR label1 +---------- AST ------------ +ForIn { + span: Some( + 0..52, + ), + variable: Identifier { + span: Some( + 4..7, + ), + name: "rec", + quote: None, + }, + resultset: Identifier { + span: Some( + 11..20, + ), + name: "resultset", + quote: None, + }, + body: [ + Continue { + span: Some( + 28..36, + ), + label: None, + }, + ], + label: Some( + Identifier { + span: Some( + 46..52, + ), + name: "label1", + quote: None, + }, + ), +} + + +---------- Input ---------- +WHILE counter < maximum_count DO + CONTINUE label1; +END WHILE label1 +---------- Output --------- +WHILE (counter < maximum_count) DO + CONTINUE label1; +END WHILE label1 +---------- AST ------------ +WhileLoop { + span: Some( + 0..70, + ), + condition: BinaryOp { + span: Some( + 14..15, + ), + op: Lt, + left: ColumnRef { + span: Some( + 6..13, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 6..13, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: ColumnRef { + span: Some( + 16..29, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 16..29, + ), + name: "maximum_count", + quote: None, + }, + ), + }, + }, + }, + body: [ + Continue { + span: Some( + 37..52, + ), + label: Some( + Identifier { + span: Some( + 46..52, + ), + name: "label1", + quote: None, + }, + ), + }, + ], + label: Some( + Identifier { + span: Some( + 64..70, + ), + name: "label1", + quote: None, + }, + ), +} + + +---------- Input ---------- +REPEAT + BREAK; +UNTIL counter = maximum_count +END REPEAT label1 +---------- Output --------- +REPEAT + BREAK; +UNTIL (counter = maximum_count) +END REPEAT label1 +---------- AST ------------ +RepeatLoop { + span: Some( + 0..65, + ), + body: [ + Break { + span: Some( + 11..16, + ), + label: None, + }, + ], + until_condition: BinaryOp { + span: Some( + 32..33, + ), + op: Eq, + left: ColumnRef { + span: Some( + 24..31, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 24..31, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: ColumnRef { + span: Some( + 34..47, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 34..47, + ), + name: "maximum_count", + quote: None, + }, + ), + }, + }, + }, + label: Some( + Identifier { + span: Some( + 59..65, + ), + name: "label1", + quote: None, + }, + ), +} + + +---------- Input ---------- +LOOP + BREAK label1; +END LOOP label1 +---------- Output --------- +LOOP + BREAK label1; +END LOOP label1 +---------- AST ------------ +Loop { + span: Some( + 0..38, + ), + body: [ + Break { + span: Some( + 9..21, + ), + label: Some( + Identifier { + span: Some( + 15..21, + ), + name: "label1", + quote: None, + }, + ), + }, + ], + label: Some( + Identifier { + span: Some( + 32..38, + ), + name: "label1", + quote: None, + }, + ), +} + + +---------- Input ---------- +CASE + WHEN counter = 1 THEN + counter := counter + 1; + WHEN counter = 2 THEN + counter := counter + 2; + ELSE + counter := counter + 3; +END +---------- Output --------- +CASE + WHEN (counter = 1) THEN + counter := (counter + 1); + WHEN (counter = 2) THEN + counter := (counter + 2); + ELSE + counter := (counter + 3); +END CASE +---------- AST ------------ +Case { + span: Some( + 0..165, + ), + operand: None, + conditions: [ + BinaryOp { + span: Some( + 22..23, + ), + op: Eq, + left: ColumnRef { + span: Some( + 14..21, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 14..21, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 24..25, + ), + lit: UInt64( + 1, + ), + }, + }, + BinaryOp { + span: Some( + 80..81, + ), + op: Eq, + left: ColumnRef { + span: Some( + 72..79, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 72..79, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 82..83, + ), + lit: UInt64( + 2, + ), + }, + }, + ], + results: [ + [ + Assign { + span: Some( + 39..61, + ), + name: Identifier { + span: Some( + 39..46, + ), + name: "counter", + quote: None, + }, + value: BinaryOp { + span: Some( + 58..59, + ), + op: Plus, + left: ColumnRef { + span: Some( + 50..57, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 50..57, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 60..61, + ), + lit: UInt64( + 1, + ), + }, + }, + }, + ], + [ + Assign { + span: Some( + 97..119, + ), + name: Identifier { + span: Some( + 97..104, + ), + name: "counter", + quote: None, + }, + value: BinaryOp { + span: Some( + 116..117, + ), + op: Plus, + left: ColumnRef { + span: Some( + 108..115, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 108..115, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 118..119, + ), + lit: UInt64( + 2, + ), + }, + }, + }, + ], + ], + else_result: Some( + [ + Assign { + span: Some( + 138..160, + ), + name: Identifier { + span: Some( + 138..145, + ), + name: "counter", + quote: None, + }, + value: BinaryOp { + span: Some( + 157..158, + ), + op: Plus, + left: ColumnRef { + span: Some( + 149..156, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 149..156, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 159..160, + ), + lit: UInt64( + 3, + ), + }, + }, + }, + ], + ), +} + + +---------- Input ---------- +CASE counter + WHEN 1 THEN + counter := counter + 1; + WHEN 2 THEN + counter := counter + 2; + ELSE + counter := counter + 3; +END CASE +---------- Output --------- +CASE counter + WHEN 1 THEN + counter := (counter + 1); + WHEN 2 THEN + counter := (counter + 2); + ELSE + counter := (counter + 3); +END CASE +---------- AST ------------ +Case { + span: Some( + 0..158, + ), + operand: Some( + ColumnRef { + span: Some( + 5..12, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 5..12, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + ), + conditions: [ + Literal { + span: Some( + 22..23, + ), + lit: UInt64( + 1, + ), + }, + Literal { + span: Some( + 70..71, + ), + lit: UInt64( + 2, + ), + }, + ], + results: [ + [ + Assign { + span: Some( + 37..59, + ), + name: Identifier { + span: Some( + 37..44, + ), + name: "counter", + quote: None, + }, + value: BinaryOp { + span: Some( + 56..57, + ), + op: Plus, + left: ColumnRef { + span: Some( + 48..55, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 48..55, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 58..59, + ), + lit: UInt64( + 1, + ), + }, + }, + }, + ], + [ + Assign { + span: Some( + 85..107, + ), + name: Identifier { + span: Some( + 85..92, + ), + name: "counter", + quote: None, + }, + value: BinaryOp { + span: Some( + 104..105, + ), + op: Plus, + left: ColumnRef { + span: Some( + 96..103, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 96..103, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 106..107, + ), + lit: UInt64( + 2, + ), + }, + }, + }, + ], + ], + else_result: Some( + [ + Assign { + span: Some( + 126..148, + ), + name: Identifier { + span: Some( + 126..133, + ), + name: "counter", + quote: None, + }, + value: BinaryOp { + span: Some( + 145..146, + ), + op: Plus, + left: ColumnRef { + span: Some( + 137..144, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 137..144, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 147..148, + ), + lit: UInt64( + 3, + ), + }, + }, + }, + ], + ), +} + + +---------- Input ---------- +IF counter = 1 THEN + counter := counter + 1; +ELSEIF counter = 2 THEN + counter := counter + 2; +ELSE + counter := counter + 3; +END IF +---------- Output --------- +IF (counter = 1) THEN + counter := (counter + 1); +ELSEIF (counter = 2) THEN + counter := (counter + 2); +ELSE + counter := (counter + 3); +END IF +---------- AST ------------ +If { + span: Some( + 0..139, + ), + conditions: [ + BinaryOp { + span: Some( + 11..12, + ), + op: Eq, + left: ColumnRef { + span: Some( + 3..10, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 3..10, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 13..14, + ), + lit: UInt64( + 1, + ), + }, + }, + BinaryOp { + span: Some( + 63..64, + ), + op: Eq, + left: ColumnRef { + span: Some( + 55..62, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 55..62, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 65..66, + ), + lit: UInt64( + 2, + ), + }, + }, + ], + results: [ + [ + Assign { + span: Some( + 24..46, + ), + name: Identifier { + span: Some( + 24..31, + ), + name: "counter", + quote: None, + }, + value: BinaryOp { + span: Some( + 43..44, + ), + op: Plus, + left: ColumnRef { + span: Some( + 35..42, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 35..42, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 45..46, + ), + lit: UInt64( + 1, + ), + }, + }, + }, + ], + [ + Assign { + span: Some( + 76..98, + ), + name: Identifier { + span: Some( + 76..83, + ), + name: "counter", + quote: None, + }, + value: BinaryOp { + span: Some( + 95..96, + ), + op: Plus, + left: ColumnRef { + span: Some( + 87..94, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 87..94, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 97..98, + ), + lit: UInt64( + 2, + ), + }, + }, + }, + ], + ], + else_result: Some( + [ + Assign { + span: Some( + 109..131, + ), + name: Identifier { + span: Some( + 109..116, + ), + name: "counter", + quote: None, + }, + value: BinaryOp { + span: Some( + 128..129, + ), + op: Plus, + left: ColumnRef { + span: Some( + 120..127, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 120..127, + ), + name: "counter", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 130..131, + ), + lit: UInt64( + 3, + ), + }, + }, + }, + ], + ), +} + + +---------- Input ---------- +LOOP + SELECT c1, c2 FROM t WHERE c1 = 1; +END LOOP +---------- Output --------- +LOOP + SELECT c1, c2 FROM t WHERE (c1 = 1); +END LOOP +---------- AST ------------ +Loop { + span: Some( + 0..52, + ), + body: [ + SQLStatement { + span: Some( + 9..42, + ), + stmt: Query( + Query { + span: Some( + 9..42, + ), + with: None, + body: Select( + SelectStmt { + span: Some( + 9..42, + ), + hints: None, + distinct: false, + select_list: [ + AliasedExpr { + expr: ColumnRef { + span: Some( + 16..18, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 16..18, + ), + name: "c1", + quote: None, + }, + ), + }, + }, + alias: None, + }, + AliasedExpr { + expr: ColumnRef { + span: Some( + 20..22, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 20..22, + ), + name: "c2", + quote: None, + }, + ), + }, + }, + alias: None, + }, + ], + from: [ + Table { + span: Some( + 28..29, + ), + catalog: None, + database: None, + table: Identifier { + span: Some( + 28..29, + ), + name: "t", + quote: None, + }, + alias: None, + travel_point: None, + since_point: None, + pivot: None, + unpivot: None, + }, + ], + selection: Some( + BinaryOp { + span: Some( + 39..40, + ), + op: Eq, + left: ColumnRef { + span: Some( + 36..38, + ), + column: ColumnRef { + database: None, + table: None, + column: Name( + Identifier { + span: Some( + 36..38, + ), + name: "c1", + quote: None, + }, + ), + }, + }, + right: Literal { + span: Some( + 41..42, + ), + lit: UInt64( + 1, + ), + }, + }, + ), + group_by: None, + having: None, + window_list: None, + qualify: None, + }, + ), + order_by: [], + limit: [], + offset: None, + ignore_result: false, + }, + ), + }, + ], + label: None, +} + + diff --git a/src/query/ast/tests/it/testdata/statement.txt b/src/query/ast/tests/it/testdata/statement.txt index 38fe9165706d4..91eb24eed0ceb 100644 --- a/src/query/ast/tests/it/testdata/statement.txt +++ b/src/query/ast/tests/it/testdata/statement.txt @@ -8167,7 +8167,7 @@ Insert( ---------- Input ---------- -insert into t (c1, c2) values (1, 2); +insert into t (c1, c2) values (1, 2); ---------- Output --------- INSERT INTO t (c1, c2) VALUES (1, 2) ---------- AST ------------