diff --git a/Cargo.lock b/Cargo.lock index 345399bc15ab..e2536a58315e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2956,6 +2956,7 @@ dependencies = [ "bincode 2.0.0-rc.3", "codespan-reporting", "databend-common-arrow", + "geos", "geozero", "http", "opendal", @@ -2998,6 +2999,7 @@ dependencies = [ "ethnum 1.5.0 (git+https://github.com/ariesdevil/ethnum-rs?rev=4cb05f1)", "futures", "geo", + "geos", "geozero", "goldenfile", "hex", @@ -3041,6 +3043,7 @@ dependencies = [ "databend-common-settings", "databend-storages-common-blocks", "databend-storages-common-table-meta", + "geos", "geozero", "hex", "jsonb 0.3.0 (git+https://github.com/datafuselabs/jsonb?rev=a7325f4)", @@ -3174,6 +3177,7 @@ dependencies = [ "databend-common-exception", "ethnum 1.5.0 (git+https://github.com/ariesdevil/ethnum-rs?rev=4cb05f1)", "geo", + "geos", "geozero", "lexical-core", "micromarshal 0.5.0", @@ -3640,6 +3644,7 @@ dependencies = [ "derive-visitor", "goldenfile", "minitrace", + "tokio", "unindent", ] @@ -4595,6 +4600,7 @@ dependencies = [ "databend-common-pipeline-sinks", "databend-common-pipeline-sources", "databend-common-pipeline-transforms", + "databend-common-script", "databend-common-settings", "databend-common-sharing", "databend-common-sql", diff --git a/src/common/exception/Cargo.toml b/src/common/exception/Cargo.toml index 2e5cc3be9a10..44fbcbc42f21 100644 --- a/src/common/exception/Cargo.toml +++ b/src/common/exception/Cargo.toml @@ -23,6 +23,7 @@ arrow-schema = { workspace = true } backtrace = { git = "https://github.com/rust-lang/backtrace-rs.git", rev = "6145fe6bac65c38375f1216a565a6cc7deb89a2d" } #backtrace = "0.3.69" bincode = { workspace = true } +geos = { workspace = true } geozero = { workspace = true } http = "0.2" opendal = { workspace = true } @@ -34,3 +35,6 @@ serde_json = { workspace = true } tantivy = "0.21.1" thiserror = { workspace = true } tonic = { workspace = true } + +[package.metadata.cargo-machete] +ignored = ["geos"] diff --git a/src/common/exception/src/lib.rs b/src/common/exception/src/lib.rs index 2f3190f296a1..537f2fd433fd 100644 --- a/src/common/exception/src/lib.rs +++ b/src/common/exception/src/lib.rs @@ -25,6 +25,8 @@ pub use exception::ErrorCode; pub use exception::Result; pub use exception::ToErrorCode; pub use exception_into::SerializedError; +pub use span::merge_span; +pub use span::offset_span; pub use span::pretty_print_error; pub use span::Range; pub use span::Span; diff --git a/src/common/exception/src/span.rs b/src/common/exception/src/span.rs index cce450ed02f1..e874176ef897 100644 --- a/src/common/exception/src/span.rs +++ b/src/common/exception/src/span.rs @@ -16,6 +16,8 @@ use std::fmt::Debug; use std::fmt::Display; use std::fmt::Formatter; +pub type Span = Option; + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)] pub struct Range { pub start: u32, @@ -26,13 +28,12 @@ impl Range { pub fn start(&self) -> usize { self.start as usize } + pub fn end(&self) -> usize { self.end as usize } } -pub type Span = Option; - impl Debug for Range { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}..{}", self.start, self.end) @@ -60,6 +61,25 @@ impl From> for Range { } } +pub fn offset_span(span: Span, offset: usize) -> Span { + span.map(|range| Range { + start: range.start + offset as u32, + end: range.end + offset as u32, + }) +} + +pub fn merge_span(lhs: Span, rhs: Span) -> Span { + match (lhs, rhs) { + (Some(lhs), Some(rhs)) => Some(Range { + start: lhs.start.min(rhs.start), + end: lhs.end.max(rhs.end), + }), + (Some(lhs), None) => Some(lhs), + (None, Some(rhs)) => Some(rhs), + (None, None) => None, + } +} + pub fn pretty_print_error(source: &str, labels: Vec<(Range, String)>) -> String { use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::diagnostic::Label; diff --git a/src/common/io/Cargo.toml b/src/common/io/Cargo.toml index bacef2a7e0df..a0768fb18239 100644 --- a/src/common/io/Cargo.toml +++ b/src/common/io/Cargo.toml @@ -24,6 +24,7 @@ chrono = { workspace = true } chrono-tz = { workspace = true } ethnum = { workspace = true } geo = { workspace = true } +geos = { workspace = true } geozero = { workspace = true } lexical-core = "0.8.5" micromarshal = "0.5.0" @@ -36,3 +37,6 @@ wkt = "0.10.3" aho-corasick = { version = "1.0.1" } rand = { workspace = true } rmp-serde = "1.1.1" + +[package.metadata.cargo-machete] +ignored = ["geos"] diff --git a/src/query/ast/Cargo.toml b/src/query/ast/Cargo.toml index 568367a3ca03..62cce6727716 100644 --- a/src/query/ast/Cargo.toml +++ b/src/query/ast/Cargo.toml @@ -34,6 +34,7 @@ serde_json = { workspace = true } strsim = "0.10" strum = "0.24" strum_macros = "0.24" +unindent = "0.2.3" url = "2.3.1" [dev-dependencies] @@ -42,7 +43,6 @@ 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/expr.rs b/src/query/ast/src/ast/expr.rs index 0a0f1d1f2f11..a221dbcc3d0e 100644 --- a/src/query/ast/src/ast/expr.rs +++ b/src/query/ast/src/ast/expr.rs @@ -15,6 +15,7 @@ use std::fmt::Display; use std::fmt::Formatter; +use databend_common_exception::merge_span; use databend_common_exception::ErrorCode; use databend_common_exception::Result; use databend_common_exception::Span; @@ -305,6 +306,139 @@ impl Expr { } } + pub fn whole_span(&self) -> Span { + match self { + Expr::ColumnRef { span, .. } => *span, + Expr::IsNull { span, expr, .. } => merge_span(*span, expr.whole_span()), + Expr::IsDistinctFrom { + span, left, right, .. + } => merge_span(merge_span(*span, left.whole_span()), right.whole_span()), + Expr::InList { + span, expr, list, .. + } => { + let mut span = merge_span(*span, expr.whole_span()); + for item in list { + span = merge_span(span, item.whole_span()); + } + span + } + Expr::InSubquery { + span, + expr, + subquery, + .. + } => merge_span(merge_span(*span, expr.whole_span()), subquery.span), + Expr::Between { + span, + expr, + low, + high, + .. + } => merge_span( + merge_span(*span, expr.whole_span()), + merge_span(low.whole_span(), high.whole_span()), + ), + Expr::BinaryOp { + span, left, right, .. + } => merge_span(merge_span(*span, left.whole_span()), right.whole_span()), + Expr::JsonOp { + span, left, right, .. + } => merge_span(merge_span(*span, left.whole_span()), right.whole_span()), + Expr::UnaryOp { span, expr, .. } => merge_span(*span, expr.whole_span()), + Expr::Cast { span, expr, .. } => merge_span(*span, expr.whole_span()), + Expr::TryCast { span, expr, .. } => merge_span(*span, expr.whole_span()), + Expr::Extract { span, expr, .. } => merge_span(*span, expr.whole_span()), + Expr::DatePart { span, expr, .. } => merge_span(*span, expr.whole_span()), + Expr::Position { + span, + substr_expr, + str_expr, + .. + } => merge_span( + merge_span(*span, substr_expr.whole_span()), + str_expr.whole_span(), + ), + Expr::Substring { + span, + expr, + substring_from, + substring_for, + .. + } => { + let mut span = merge_span( + merge_span(*span, expr.whole_span()), + substring_from.whole_span(), + ); + if let Some(substring_for) = substring_for { + span = merge_span(span, substring_for.whole_span()); + } + span + } + Expr::Trim { span, expr, .. } => merge_span(*span, expr.whole_span()), + Expr::Literal { span, .. } => *span, + Expr::CountAll { span, .. } => *span, + Expr::Tuple { span, exprs } => { + let mut span = *span; + for expr in exprs { + span = merge_span(span, expr.whole_span()); + } + span + } + Expr::FunctionCall { span, .. } => *span, + Expr::Case { + span, + operand, + conditions, + results, + else_result, + } => { + let mut span = *span; + if let Some(operand) = operand { + span = merge_span(span, operand.whole_span()); + } + for (cond, res) in conditions.iter().zip(results) { + span = merge_span(merge_span(span, cond.whole_span()), res.whole_span()); + } + if let Some(else_result) = else_result { + span = merge_span(span, else_result.whole_span()); + } + span + } + Expr::Exists { span, subquery, .. } => merge_span(*span, subquery.span), + Expr::Subquery { span, subquery, .. } => merge_span(*span, subquery.span), + Expr::MapAccess { span, expr, .. } => merge_span(*span, expr.whole_span()), + Expr::Array { span, exprs } => { + let mut span = *span; + for expr in exprs { + span = merge_span(span, expr.whole_span()); + } + span + } + Expr::Map { span, kvs } => { + let mut span = *span; + for (_, v) in kvs { + span = merge_span(span, v.whole_span()); + } + span + } + Expr::Interval { span, expr, .. } => merge_span(*span, expr.whole_span()), + Expr::DateAdd { + span, + interval, + date, + .. + } => merge_span(merge_span(*span, interval.whole_span()), date.whole_span()), + Expr::DateSub { + span, + interval, + date, + .. + } => merge_span(merge_span(*span, interval.whole_span()), date.whole_span()), + Expr::DateTrunc { span, date, .. } => merge_span(*span, date.whole_span()), + Expr::Hole { span, .. } => *span, + } + } + pub fn all_function_like_syntaxes() -> &'static [&'static str] { &[ "CAST", diff --git a/src/query/ast/src/ast/statements/mod.rs b/src/query/ast/src/ast/statements/mod.rs index d478a8865905..09b9227ca2b7 100644 --- a/src/query/ast/src/ast/statements/mod.rs +++ b/src/query/ast/src/ast/statements/mod.rs @@ -33,6 +33,7 @@ mod notification; mod password_policy; mod pipe; mod presign; +mod procedure; mod replace; mod script; mod share; @@ -70,6 +71,7 @@ pub use notification::*; pub use password_policy::*; pub use pipe::*; pub use presign::*; +pub use procedure::*; pub use replace::*; pub use script::*; pub use share::*; diff --git a/src/query/ast/src/ast/statements/procedure.rs b/src/query/ast/src/ast/statements/procedure.rs new file mode 100644 index 000000000000..ef4c8fd135a0 --- /dev/null +++ b/src/query/ast/src/ast/statements/procedure.rs @@ -0,0 +1,32 @@ +// 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 derive_visitor::Drive; +use derive_visitor::DriveMut; + +#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)] +pub struct ExecuteImmediateStmt { + #[drive(skip)] + pub script: String, +} + +impl Display for ExecuteImmediateStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "EXECUTE IMMEDIATE $$\n{}\n$$", self.script)?; + Ok(()) + } +} diff --git a/src/query/ast/src/ast/statements/script.rs b/src/query/ast/src/ast/statements/script.rs index b6684e3821e6..1317f2b0daa1 100644 --- a/src/query/ast/src/ast/statements/script.rs +++ b/src/query/ast/src/ast/statements/script.rs @@ -20,70 +20,107 @@ use databend_common_exception::Span; use crate::ast::Expr; use crate::ast::Identifier; 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, +pub struct ScriptBlock { + pub span: Span, + pub declares: Vec, + pub body: Vec, +} + +impl Display for ScriptBlock { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "DECLARE")?; + for declare in &self.declares { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH, format!("{};", declare)) + )?; + } + writeln!(f, "BEGIN")?; + for stmt in &self.body { + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH, format!("{};", stmt)) + )?; + } + writeln!(f, "END;")?; + Ok(()) + } } #[derive(Debug, Clone, PartialEq)] -pub struct ScriptBody { - declare: Vec, - body: Vec, - exception_body: Option, +pub enum DeclareItem { + Var(DeclareVar), + Set(DeclareSet), +} + +impl Display for DeclareItem { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + DeclareItem::Var(declare) => write!(f, "{declare}"), + DeclareItem::Set(declare) => write!(f, "{declare}"), + } + } } #[derive(Debug, Clone, PartialEq)] -pub struct VariableDeclare { +pub struct DeclareVar { + pub span: Span, pub name: Identifier, - pub data_type: Option, pub default: Expr, } -impl Display for VariableDeclare { +impl Display for DeclareVar { 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}")?; - } + let DeclareVar { name, default, .. } = self; + write!(f, "{name} := {default}")?; Ok(()) } } #[derive(Debug, Clone, PartialEq)] -pub struct StatementDeclare { +pub struct DeclareSet { + pub span: Span, pub name: Identifier, pub stmt: Statement, } -impl Display for StatementDeclare { +impl Display for DeclareSet { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let StatementDeclare { name, stmt } = self; + let DeclareSet { name, stmt, .. } = self; write!(f, "{name} RESULTSET := {stmt}") } } +#[derive(Debug, Clone, PartialEq)] +pub enum ReturnItem { + Var(Expr), + Set(Identifier), + Statement(Statement), +} + +impl Display for ReturnItem { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ReturnItem::Var(expr) => write!(f, "{expr}"), + ReturnItem::Set(name) => write!(f, "TABLE({name})"), + ReturnItem::Statement(stmt) => write!(f, "TABLE({stmt})"), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum ScriptStatement { LetVar { - span: Span, - declare: VariableDeclare, + declare: DeclareVar, }, LetStatement { - span: Span, - declare: StatementDeclare, + declare: DeclareSet, }, RunStatement { span: Span, @@ -96,7 +133,7 @@ pub enum ScriptStatement { }, Return { span: Span, - value: Option, + value: Option, }, ForLoop { span: Span, @@ -107,13 +144,20 @@ pub enum ScriptStatement { body: Vec, label: Option, }, - ForIn { + ForInSet { span: Span, variable: Identifier, resultset: Identifier, body: Vec, label: Option, }, + ForInStatement { + span: Span, + variable: Identifier, + stmt: Statement, + body: Vec, + label: Option, + }, WhileLoop { span: Span, condition: Expr, @@ -195,7 +239,7 @@ impl Display for ScriptStatement { } Ok(()) } - ScriptStatement::ForIn { + ScriptStatement::ForInSet { variable, resultset, body, @@ -216,6 +260,33 @@ impl Display for ScriptStatement { } Ok(()) } + ScriptStatement::ForInStatement { + variable, + stmt, + body, + label, + .. + } => { + writeln!(f, "FOR {variable} IN")?; + writeln!( + f, + "{}", + indent::indent_all_by(INDENT_DEPTH, format!("{stmt}")) + )?; + writeln!(f, "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, diff --git a/src/query/ast/src/ast/statements/statement.rs b/src/query/ast/src/ast/statements/statement.rs index 968d668e6212..81fe7081ad7f 100644 --- a/src/query/ast/src/ast/statements/statement.rs +++ b/src/query/ast/src/ast/statements/statement.rs @@ -325,6 +325,9 @@ pub enum Statement { AlterNotification(AlterNotificationStmt), DropNotification(DropNotificationStmt), DescribeNotification(DescribeNotificationStmt), + + // Stored procedures + ExecuteImmediate(ExecuteImmediateStmt), } #[derive(Debug, Clone, PartialEq)] @@ -716,6 +719,8 @@ impl Display for Statement { Statement::AlterNotification(stmt) => write!(f, "{stmt}")?, Statement::DropNotification(stmt) => write!(f, "{stmt}")?, Statement::DescribeNotification(stmt) => write!(f, "{stmt}")?, + + Statement::ExecuteImmediate(stmt) => write!(f, "{stmt}")?, } Ok(()) } diff --git a/src/query/ast/src/ast/statements/udf.rs b/src/query/ast/src/ast/statements/udf.rs index 656cb8fb337e..6a1206718341 100644 --- a/src/query/ast/src/ast/statements/udf.rs +++ b/src/query/ast/src/ast/statements/udf.rs @@ -92,7 +92,7 @@ impl Display for UDFDefinition { write_comma_separated_list(f, arg_types)?; write!( f, - ") RETURNS {return_type} LANGUAGE {language} HANDLER = '{handler}' AS $${code}$$" + ") RETURNS {return_type} LANGUAGE {language} HANDLER = '{handler}' AS $$\n{code}\n$$" )?; } } diff --git a/src/query/ast/src/ast/visitors/walk.rs b/src/query/ast/src/ast/visitors/walk.rs index 260465609251..c7ce6c2b2af0 100644 --- a/src/query/ast/src/ast/visitors/walk.rs +++ b/src/query/ast/src/ast/visitors/walk.rs @@ -585,5 +585,6 @@ pub fn walk_statement<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Statem Statement::Commit => {} Statement::Abort => {} Statement::InsertMultiTable(_) => {} + Statement::ExecuteImmediate(_) => {} } } diff --git a/src/query/ast/src/ast/visitors/walk_mut.rs b/src/query/ast/src/ast/visitors/walk_mut.rs index 342fa478df60..937632de9ab4 100644 --- a/src/query/ast/src/ast/visitors/walk_mut.rs +++ b/src/query/ast/src/ast/visitors/walk_mut.rs @@ -583,5 +583,6 @@ pub fn walk_statement_mut(visitor: &mut V, statement: &mut Statem Statement::DropNotification(stmt) => visitor.visit_drop_notification(stmt), Statement::DescribeNotification(stmt) => visitor.visit_describe_notification(stmt), Statement::InsertMultiTable(_) => {} + Statement::ExecuteImmediate(_) => {} } } diff --git a/src/query/ast/src/parser/error.rs b/src/query/ast/src/parser/error.rs index fea4fcba8358..afbbd46ab2f0 100644 --- a/src/query/ast/src/parser/error.rs +++ b/src/query/ast/src/parser/error.rs @@ -223,7 +223,7 @@ pub fn display_parser_error(error: Error, source: &str) -> String { }); let mut msg = if span_text.is_empty() { - "unexpected end of line".to_string() + "unexpected end of input".to_string() } else { format!("unexpected `{span_text}`") }; diff --git a/src/query/ast/src/parser/expr.rs b/src/query/ast/src/parser/expr.rs index 462f762399b4..66190a3fe1de 100644 --- a/src/query/ast/src/parser/expr.rs +++ b/src/query/ast/src/parser/expr.rs @@ -1516,8 +1516,9 @@ pub fn at_string(i: Input) -> IResult { pub fn code_string(i: Input) -> IResult { map_res(rule! { CodeString }, |token| { - let path = &token.text()[2..token.text().len() - 2]; - Ok(path.to_string()) + let content = &token.text()[2..token.text().len() - 2]; + let trimmed = unindent::unindent(content).trim().to_string(); + Ok(trimmed) })(i) } diff --git a/src/query/ast/src/parser/script.rs b/src/query/ast/src/parser/script.rs index 611cbb477aca..c360257b4a86 100644 --- a/src/query/ast/src/parser/script.rs +++ b/src/query/ast/src/parser/script.rs @@ -23,32 +23,78 @@ use crate::parser::statement::*; use crate::parser::token::*; use crate::rule; -pub fn script_stmts(i: Input) -> IResult> { - semicolon_terminated_list1(script_stmt)(i) +pub fn script_block(i: Input) -> IResult { + map( + consumed(rule! { + ( DECLARE ~ #semicolon_terminated_list1(declare_item) )? + ~ BEGIN + ~ #semicolon_terminated_list1(script_stmt) + ~ END + ~ ";" + }), + |(span, (declares, _, body, _, _))| { + let declares = declares.map(|(_, declare)| declare).unwrap_or_default(); + ScriptBlock { + span: transform_span(span.tokens), + declares, + body, + } + }, + )(i) } -pub fn script_stmt(i: Input) -> IResult { - let let_stmt_stmt = map( +pub fn declare_item(i: Input) -> IResult { + let declare_var = map(declare_var, DeclareItem::Var); + let declare_set = map(declare_set, DeclareItem::Set); + + rule!( + #declare_var + | #declare_set + )(i) +} + +pub fn declare_var(i: Input) -> IResult { + map( consumed(rule! { - LET ~^#ident ~ RESULTSET ~ ^":=" ~ ^#statement_body + #ident ~ ":=" ~ ^#expr }), - |(span, (_, name, _, _, stmt))| ScriptStatement::LetStatement { + |(span, (name, _, default))| DeclareVar { span: transform_span(span.tokens), - declare: StatementDeclare { name, stmt }, + name, + default, }, - ); - let let_var_stmt = map( + )(i) +} + +pub fn declare_set(i: Input) -> IResult { + map( consumed(rule! { - LET ~^#ident ~ #type_name? ~ ^":=" ~ ^#expr + #ident ~ RESULTSET ~ ^":=" ~ ^#statement_body }), - |(span, (_, name, data_type, _, default))| ScriptStatement::LetVar { + |(span, (name, _, _, stmt))| DeclareSet { span: transform_span(span.tokens), - declare: VariableDeclare { - name, - data_type, - default, - }, + name, + stmt, + }, + )(i) +} + +pub fn script_stmts(i: Input) -> IResult> { + semicolon_terminated_list1(script_stmt)(i) +} + +pub fn script_stmt(i: Input) -> IResult { + let let_var_stmt = map( + rule! { + LET ~ #declare_var + }, + |(_, declare)| ScriptStatement::LetVar { declare }, + ); + let let_stmt_stmt = map( + rule! { + LET ~ #declare_set }, + |(_, declare)| ScriptStatement::LetStatement { declare }, ); let run_stmt = map( consumed(rule! { @@ -69,13 +115,40 @@ pub fn script_stmt(i: Input) -> IResult { value, }, ); + let return_set_stmt = map( + consumed(rule! { + RETURN ~ TABLE ~ "(" ~ #ident ~ ^")" + }), + |(span, (_, _, _, name, _))| ScriptStatement::Return { + span: transform_span(span.tokens), + value: Some(ReturnItem::Set(name)), + }, + ); + let return_stmt_stmt = map( + consumed(rule! { + RETURN ~ TABLE ~ "(" ~ #statement_body ~ ^")" + }), + |(span, (_, _, _, stmt, _))| ScriptStatement::Return { + span: transform_span(span.tokens), + value: Some(ReturnItem::Statement(stmt)), + }, + ); + let return_var_stmt = map( + consumed(rule! { + RETURN ~ #expr + }), + |(span, (_, expr))| ScriptStatement::Return { + span: transform_span(span.tokens), + value: Some(ReturnItem::Var(expr)), + }, + ); let return_stmt = map( consumed(rule! { - RETURN ~ #expr? + RETURN }), - |(span, (_, value))| ScriptStatement::Return { + |(span, _)| ScriptStatement::Return { span: transform_span(span.tokens), - value, + value: None, }, ); let for_loop_stmt = map( @@ -98,13 +171,13 @@ pub fn script_stmt(i: Input) -> IResult { label, }, ); - let for_in_stmt = map( + let for_in_set_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, (_, variable, _, resultset, _, body, _, _, label))| ScriptStatement::ForInSet { span: transform_span(span.tokens), variable, resultset, @@ -112,6 +185,20 @@ pub fn script_stmt(i: Input) -> IResult { label, }, ); + let for_in_stmt_stmt = map( + consumed(rule! { + FOR ~ ^#ident ~ ^IN ~ ^#statement_body ~ ^DO + ~ ^#semicolon_terminated_list1(script_stmt) + ~ ^END ~ ^FOR ~ #ident? + }), + |(span, (_, variable, _, stmt, _, body, _, _, label))| ScriptStatement::ForInStatement { + span: transform_span(span.tokens), + variable, + stmt, + body, + label, + }, + ); let while_loop_stmt = map( consumed(rule! { WHILE ~ ^#expr ~ ^DO @@ -217,9 +304,13 @@ pub fn script_stmt(i: Input) -> IResult { | #let_var_stmt | #run_stmt | #assign_stmt + | #return_set_stmt + | #return_stmt_stmt + | #return_var_stmt | #return_stmt | #for_loop_stmt - | #for_in_stmt + | #for_in_set_stmt + | #for_in_stmt_stmt | #while_loop_stmt | #repeat_loop_stmt | #loop_stmt diff --git a/src/query/ast/src/parser/statement.rs b/src/query/ast/src/parser/statement.rs index 28e294b06e30..5c64e939b143 100644 --- a/src/query/ast/src/parser/statement.rs +++ b/src/query/ast/src/parser/statement.rs @@ -2025,6 +2025,13 @@ pub fn statement_body(i: Input) -> IResult { let commit = value(Statement::Commit, rule! { COMMIT }); let abort = value(Statement::Abort, rule! { ABORT | ROLLBACK }); + let execute_immediate = map( + rule! { + EXECUTE ~ IMMEDIATE ~ #code_string + }, + |(_, _, script)| Statement::ExecuteImmediate(ExecuteImmediateStmt { script }), + ); + alt(( // query, explain,show rule!( @@ -2149,24 +2156,16 @@ pub fn statement_body(i: Input) -> IResult { | #list_stage: "`LIST @ [pattern = '']`" | #remove_stage: "`REMOVE @ [pattern = '']`" | #drop_stage: "`DROP STAGE `" - ), - rule!( - #create_file_format: "`CREATE FILE FORMAT [ IF NOT EXISTS ] formatTypeOptions`" + | #create_file_format: "`CREATE FILE FORMAT [ IF NOT EXISTS ] formatTypeOptions`" | #show_file_formats: "`SHOW FILE FORMATS`" | #drop_file_format: "`DROP FILE FORMAT [ IF EXISTS ] `" - ), - rule!( #copy_into ), - rule!( - #call: "`CALL (, ...)`" - ), - rule!( - #grant : "`GRANT { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } TO { [ROLE ] | [USER] }`" + | #copy_into + | #call: "`CALL (, ...)`" + | #grant : "`GRANT { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } TO { [ROLE ] | [USER] }`" | #show_grants : "`SHOW GRANTS {FOR { ROLE | USER }] | ON {DATABASE | TABLE .} }`" | #revoke : "`REVOKE { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } FROM { [ROLE ] | [USER] }`" | #grant_ownership : "GRANT OWNERSHIP ON TO ROLE " - ), - rule!( - #presign: "`PRESIGN [{DOWNLOAD | UPLOAD}] [EXPIRE = 3600]`" + | #presign: "`PRESIGN [{DOWNLOAD | UPLOAD}] [EXPIRE = 3600]`" ), // data mask rule!( @@ -2231,9 +2230,10 @@ AS ), rule!( #create_connection: "`CREATE [OR REPLACE] CONNECTION [IF NOT EXISTS] STORAGE_TYPE = `" - | #drop_connection: "`DROP CONNECTION [IF EXISTS] `" - | #desc_connection: "`DESC | DESCRIBE CONNECTION `" - | #show_connections: "`SHOW CONNECTIONS`" + | #drop_connection: "`DROP CONNECTION [IF EXISTS] `" + | #desc_connection: "`DESC | DESCRIBE CONNECTION `" + | #show_connections: "`SHOW CONNECTIONS`" + | #execute_immediate : "`EXECUTE IMMEDIATE $$