Skip to content

Commit dd3916a

Browse files
authored
feat(query): add THROW support to SQL procedures (#19067)
1 parent 4ff198a commit dd3916a

File tree

11 files changed

+123
-0
lines changed

11 files changed

+123
-0
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ pub enum ScriptStatement {
207207
span: Span,
208208
value: Option<ReturnItem>,
209209
},
210+
Throw {
211+
span: Span,
212+
message: Option<Expr>,
213+
},
210214
ForLoop {
211215
span: Span,
212216
variable: Identifier,
@@ -290,6 +294,13 @@ impl Display for ScriptStatement {
290294
write!(f, "RETURN")
291295
}
292296
}
297+
ScriptStatement::Throw { message, .. } => {
298+
if let Some(message) = message {
299+
write!(f, "THROW {message}")
300+
} else {
301+
write!(f, "THROW")
302+
}
303+
}
293304
ScriptStatement::ForLoop {
294305
variable,
295306
is_reverse,

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,15 @@ pub fn script_stmt(i: Input) -> IResult<ScriptStatement> {
269269
value: None,
270270
},
271271
);
272+
let throw_stmt = map(
273+
consumed(rule! {
274+
THROW ~ #expr?
275+
}),
276+
|(span, (_, message))| ScriptStatement::Throw {
277+
span: transform_span(span.tokens),
278+
message,
279+
},
280+
);
272281
let for_loop_stmt = map(
273282
consumed(rule! {
274283
FOR ~ ^#ident ~ ^IN ~ REVERSE?
@@ -439,6 +448,7 @@ pub fn script_stmt(i: Input) -> IResult<ScriptStatement> {
439448
| #return_stmt_stmt
440449
| #return_var_stmt
441450
| #return_stmt
451+
| #throw_stmt
442452
| #break_stmt
443453
| #continue_stmt
444454
);

src/query/ast/src/parser/token.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,8 @@ pub enum TokenKind {
12401240
TENANT,
12411241
#[token("THEN", ignore(ascii_case))]
12421242
THEN,
1243+
#[token("THROW", ignore(ascii_case))]
1244+
THROW,
12431245
#[token("THURSDAY", ignore(ascii_case))]
12441246
THURSDAY,
12451247
#[token("TIME", ignore(ascii_case))]

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,6 +1555,7 @@ fn test_script() {
15551555
r#"RETURN profit"#,
15561556
r#"RETURN TABLE(t1)"#,
15571557
r#"RETURN TABLE(select count(*) from t1)"#,
1558+
r#"THROW 'Email already exists.'"#,
15581559
r#"
15591560
FOR i IN REVERSE 1 TO maximum_count DO
15601561
counter := counter + 1;

src/query/ast/tests/it/testdata/script.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,28 @@ Return {
536536
}
537537

538538

539+
---------- Input ----------
540+
THROW 'Email already exists.'
541+
---------- Output ---------
542+
THROW 'Email already exists.'
543+
---------- AST ------------
544+
Throw {
545+
span: Some(
546+
0..29,
547+
),
548+
message: Some(
549+
Literal {
550+
span: Some(
551+
6..29,
552+
),
553+
value: String(
554+
"Email already exists.",
555+
),
556+
},
557+
),
558+
}
559+
560+
539561
---------- Input ----------
540562
FOR i IN REVERSE 1 TO maximum_count DO
541563
counter := counter + 1;

src/query/script/src/compiler.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,22 @@ impl Compiler {
193193
output.append(&mut self.compile_sql_statement(*span, stmt, to_set.clone())?);
194194
output.push(ScriptIR::ReturnSet { set: to_set });
195195
}
196+
ScriptStatement::Throw { span, message } => {
197+
let mut throw_value = None;
198+
if let Some(expr) = message {
199+
let to_var = VarRef::new_internal(
200+
expr.span(),
201+
"throw_value",
202+
&mut self.ref_allocator,
203+
);
204+
output.append(&mut self.compile_expr(expr, to_var.clone())?);
205+
throw_value = Some(to_var);
206+
}
207+
output.push(ScriptIR::Throw {
208+
span: *span,
209+
message: throw_value,
210+
});
211+
}
196212
ScriptStatement::ForLoop {
197213
span,
198214
variable,

src/query/script/src/executor.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub trait Client {
3737
-> Result<Self::Var>;
3838
fn num_rows(&self, block: &Self::Set) -> usize;
3939
fn is_true(&self, scalar: &Self::Var) -> Result<bool>;
40+
fn format_error(&self, value: &Self::Var) -> Result<String>;
4041
}
4142

4243
#[derive(Debug, Clone)]
@@ -181,6 +182,15 @@ impl<C: Client> Executor<C> {
181182
self.return_value = Some(ReturnValue::Set(self.get_set(set)?.clone()));
182183
self.goto_end();
183184
}
185+
ScriptIR::Throw { span, message } => {
186+
let msg = if let Some(var) = message {
187+
let value = self.get_var(var)?;
188+
self.client.format_error(value)?
189+
} else {
190+
"Script threw an error".to_string()
191+
};
192+
return Err(ErrorCode::ScriptExecutionError(msg).set_span(*span));
193+
}
184194
}
185195

186196
self.pc += 1;

src/query/script/src/ir.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ pub enum ScriptIR {
9393
ReturnVar { var: VarRef },
9494
/// Returns a result set from the script.
9595
ReturnSet { set: SetRef },
96+
/// Throws an error from the script.
97+
Throw { span: Span, message: Option<VarRef> },
9698
}
9799

98100
impl Display for ScriptIR {
@@ -123,6 +125,13 @@ impl Display for ScriptIR {
123125
ScriptIR::Return => write!(f, "RETURN")?,
124126
ScriptIR::ReturnVar { var } => write!(f, "RETURN {var}")?,
125127
ScriptIR::ReturnSet { set } => write!(f, "RETURN {set}")?,
128+
ScriptIR::Throw { message, .. } => {
129+
if let Some(message) = message {
130+
write!(f, "THROW {message}")?;
131+
} else {
132+
write!(f, "THROW")?;
133+
}
134+
}
126135
};
127136
Ok(())
128137
}

src/query/script/tests/it/main.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,13 @@ impl Client for MockClient {
696696
fn is_true(&self, scalar: &Self::Var) -> Result<bool> {
697697
Ok(*scalar == Literal::Boolean(true))
698698
}
699+
700+
fn format_error(&self, value: &Self::Var) -> Result<String> {
701+
Ok(match value {
702+
Literal::String(s) => s.clone(),
703+
_ => value.to_string(),
704+
})
705+
}
699706
}
700707

701708
#[derive(Debug, Clone)]

src/query/service/src/interpreters/util.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use databend_common_ast::parser::tokenize_sql;
2020
use databend_common_ast::parser::Dialect;
2121
use databend_common_catalog::catalog::Catalog;
2222
use databend_common_exception::ErrorCode;
23+
use databend_common_expression::display::scalar_ref_to_string;
2324
use databend_common_expression::ComputedExpr;
2425
use databend_common_expression::DataBlock;
2526
use databend_common_expression::DataSchemaRef;
@@ -217,6 +218,13 @@ impl Client for ScriptClient {
217218
))),
218219
}
219220
}
221+
222+
fn format_error(&self, value: &Self::Var) -> databend_common_exception::Result<String> {
223+
Ok(match value {
224+
Scalar::String(s) => s.clone(),
225+
_ => scalar_ref_to_string(&value.as_ref()),
226+
})
227+
}
220228
}
221229

222230
#[derive(serde::Serialize)]

0 commit comments

Comments
 (0)