Skip to content

Commit fd39a48

Browse files
mvzinkayman-sigma
authored andcommitted
Improve MySQL CREATE TRIGGER parsing (apache#1998)
1 parent 26ceb08 commit fd39a48

File tree

7 files changed

+60
-17
lines changed

7 files changed

+60
-17
lines changed

src/ast/mod.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3991,6 +3991,15 @@ pub enum Statement {
39913991
/// EXECUTE FUNCTION trigger_function();
39923992
/// ```
39933993
period: TriggerPeriod,
3994+
/// Whether the trigger period was specified before the target table name.
3995+
///
3996+
/// ```sql
3997+
/// -- period_before_table == true: Postgres, MySQL, and standard SQL
3998+
/// CREATE TRIGGER t BEFORE INSERT ON table_name ...;
3999+
/// -- period_before_table == false: MSSQL
4000+
/// CREATE TRIGGER t ON table_name BEFORE INSERT ...;
4001+
/// ```
4002+
period_before_table: bool,
39944003
/// Multiple events can be specified using OR, such as `INSERT`, `UPDATE`, `DELETE`, or `TRUNCATE`.
39954004
events: Vec<TriggerEvent>,
39964005
/// The table on which the trigger is to be created.
@@ -4009,6 +4018,8 @@ pub enum Statement {
40094018
condition: Option<Expr>,
40104019
/// Execute logic block
40114020
exec_body: Option<TriggerExecBody>,
4021+
/// For MSSQL and dialects where statements are preceded by `AS`
4022+
statements_as: bool,
40124023
/// For SQL dialects with statement(s) for a body
40134024
statements: Option<ConditionalStatements>,
40144025
/// The characteristic of the trigger, which include whether the trigger is `DEFERRABLE`, `INITIALLY DEFERRED`, or `INITIALLY IMMEDIATE`,
@@ -4973,6 +4984,7 @@ impl fmt::Display for Statement {
49734984
or_replace,
49744985
is_constraint,
49754986
name,
4987+
period_before_table,
49764988
period,
49774989
events,
49784990
table_name,
@@ -4982,6 +4994,7 @@ impl fmt::Display for Statement {
49824994
condition,
49834995
include_each,
49844996
exec_body,
4997+
statements_as,
49854998
statements,
49864999
characteristics,
49875000
} => {
@@ -4993,7 +5006,7 @@ impl fmt::Display for Statement {
49935006
is_constraint = if *is_constraint { "CONSTRAINT " } else { "" },
49945007
)?;
49955008

4996-
if exec_body.is_some() {
5009+
if *period_before_table {
49975010
write!(f, "{period}")?;
49985011
if !events.is_empty() {
49995012
write!(f, " {}", display_separated(events, " OR "))?;
@@ -5031,7 +5044,10 @@ impl fmt::Display for Statement {
50315044
write!(f, " EXECUTE {exec_body}")?;
50325045
}
50335046
if let Some(statements) = statements {
5034-
write!(f, " AS {statements}")?;
5047+
if *statements_as {
5048+
write!(f, " AS")?;
5049+
}
5050+
write!(f, " {statements}")?;
50355051
}
50365052
Ok(())
50375053
}

src/dialect/mssql.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ impl MsSqlDialect {
257257
is_constraint: false,
258258
name,
259259
period,
260+
period_before_table: false,
260261
events,
261262
table_name,
262263
referenced_table_name: None,
@@ -265,6 +266,7 @@ impl MsSqlDialect {
265266
include_each: false,
266267
condition: None,
267268
exec_body: None,
269+
statements_as: true,
268270
statements,
269271
characteristics: None,
270272
})

src/parser/mod.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5624,25 +5624,31 @@ impl<'a> Parser<'a> {
56245624
.then(|| self.parse_expr())
56255625
.transpose()?;
56265626

5627-
self.expect_keyword_is(Keyword::EXECUTE)?;
5628-
5629-
let exec_body = self.parse_trigger_exec_body()?;
5627+
let mut exec_body = None;
5628+
let mut statements = None;
5629+
if self.parse_keyword(Keyword::EXECUTE) {
5630+
exec_body = Some(self.parse_trigger_exec_body()?);
5631+
} else {
5632+
statements = Some(self.parse_conditional_statements(&[Keyword::END])?);
5633+
}
56305634

56315635
Ok(Statement::CreateTrigger {
56325636
or_alter,
56335637
or_replace,
56345638
is_constraint,
56355639
name,
56365640
period,
5641+
period_before_table: true,
56375642
events,
56385643
table_name,
56395644
referenced_table_name,
56405645
referencing,
56415646
trigger_object,
56425647
include_each,
56435648
condition,
5644-
exec_body: Some(exec_body),
5645-
statements: None,
5649+
exec_body,
5650+
statements_as: false,
5651+
statements,
56465652
characteristics,
56475653
})
56485654
}
@@ -6568,7 +6574,7 @@ impl<'a> Parser<'a> {
65686574

65696575
let args = if self.consume_token(&Token::LParen) {
65706576
if self.consume_token(&Token::RParen) {
6571-
None
6577+
Some(vec![])
65726578
} else {
65736579
let args = self.parse_comma_separated(Parser::parse_function_arg)?;
65746580
self.expect_token(&Token::RParen)?;

tests/sqlparser_bigquery.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2806,9 +2806,9 @@ fn test_export_data() {
28062806
);
28072807

28082808
let err = bigquery()
2809-
.parse_sql_statements(concat!(
2809+
.parse_sql_statements(
28102810
"EXPORT DATA AS SELECT field1, field2 FROM mydataset.table1 ORDER BY field1 LIMIT 10",
2811-
))
2811+
)
28122812
.unwrap_err();
28132813
assert_eq!(
28142814
err.to_string(),

tests/sqlparser_mssql.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2376,6 +2376,7 @@ fn parse_create_trigger() {
23762376
is_constraint: false,
23772377
name: ObjectName::from(vec![Ident::new("reminder1")]),
23782378
period: TriggerPeriod::After,
2379+
period_before_table: false,
23792380
events: vec![TriggerEvent::Insert, TriggerEvent::Update(vec![]),],
23802381
table_name: ObjectName::from(vec![Ident::new("Sales"), Ident::new("Customer")]),
23812382
referenced_table_name: None,
@@ -2384,6 +2385,7 @@ fn parse_create_trigger() {
23842385
include_each: false,
23852386
condition: None,
23862387
exec_body: None,
2388+
statements_as: true,
23872389
statements: Some(ConditionalStatements::Sequence {
23882390
statements: vec![Statement::RaisError {
23892391
message: Box::new(Expr::Value(

tests/sqlparser_mysql.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3914,11 +3914,8 @@ fn parse_looks_like_single_line_comment() {
39143914

39153915
#[test]
39163916
fn parse_create_trigger() {
3917-
let sql_create_trigger = r#"
3918-
CREATE TRIGGER emp_stamp BEFORE INSERT ON emp
3919-
FOR EACH ROW EXECUTE FUNCTION emp_stamp();
3920-
"#;
3921-
let create_stmt = mysql().one_statement_parses_to(sql_create_trigger, "");
3917+
let sql_create_trigger = r#"CREATE TRIGGER emp_stamp BEFORE INSERT ON emp FOR EACH ROW EXECUTE FUNCTION emp_stamp()"#;
3918+
let create_stmt = mysql().verified_stmt(sql_create_trigger);
39223919
assert_eq!(
39233920
create_stmt,
39243921
Statement::CreateTrigger {
@@ -3927,6 +3924,7 @@ fn parse_create_trigger() {
39273924
is_constraint: false,
39283925
name: ObjectName::from(vec![Ident::new("emp_stamp")]),
39293926
period: TriggerPeriod::Before,
3927+
period_before_table: true,
39303928
events: vec![TriggerEvent::Insert],
39313929
table_name: ObjectName::from(vec![Ident::new("emp")]),
39323930
referenced_table_name: None,
@@ -3938,15 +3936,22 @@ fn parse_create_trigger() {
39383936
exec_type: TriggerExecBodyType::Function,
39393937
func_desc: FunctionDesc {
39403938
name: ObjectName::from(vec![Ident::new("emp_stamp")]),
3941-
args: None,
3939+
args: Some(vec![]),
39423940
}
39433941
}),
3942+
statements_as: false,
39443943
statements: None,
39453944
characteristics: None,
39463945
}
39473946
);
39483947
}
39493948

3949+
#[test]
3950+
fn parse_create_trigger_compound_statement() {
3951+
mysql_and_generic().verified_stmt("CREATE TRIGGER mytrigger BEFORE INSERT ON mytable FOR EACH ROW BEGIN SET NEW.a = 1; SET NEW.b = 2; END");
3952+
mysql_and_generic().verified_stmt("CREATE TRIGGER tr AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES (NEW.id); END");
3953+
}
3954+
39503955
#[test]
39513956
fn parse_drop_trigger() {
39523957
let sql_drop_trigger = "DROP TRIGGER emp_stamp;";

tests/sqlparser_postgres.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5552,6 +5552,7 @@ fn parse_create_simple_before_insert_trigger() {
55525552
is_constraint: false,
55535553
name: ObjectName::from(vec![Ident::new("check_insert")]),
55545554
period: TriggerPeriod::Before,
5555+
period_before_table: true,
55555556
events: vec![TriggerEvent::Insert],
55565557
table_name: ObjectName::from(vec![Ident::new("accounts")]),
55575558
referenced_table_name: None,
@@ -5566,6 +5567,7 @@ fn parse_create_simple_before_insert_trigger() {
55665567
args: None,
55675568
},
55685569
}),
5570+
statements_as: false,
55695571
statements: None,
55705572
characteristics: None,
55715573
};
@@ -5582,6 +5584,7 @@ fn parse_create_after_update_trigger_with_condition() {
55825584
is_constraint: false,
55835585
name: ObjectName::from(vec![Ident::new("check_update")]),
55845586
period: TriggerPeriod::After,
5587+
period_before_table: true,
55855588
events: vec![TriggerEvent::Update(vec![])],
55865589
table_name: ObjectName::from(vec![Ident::new("accounts")]),
55875590
referenced_table_name: None,
@@ -5603,6 +5606,7 @@ fn parse_create_after_update_trigger_with_condition() {
56035606
args: None,
56045607
},
56055608
}),
5609+
statements_as: false,
56065610
statements: None,
56075611
characteristics: None,
56085612
};
@@ -5619,6 +5623,7 @@ fn parse_create_instead_of_delete_trigger() {
56195623
is_constraint: false,
56205624
name: ObjectName::from(vec![Ident::new("check_delete")]),
56215625
period: TriggerPeriod::InsteadOf,
5626+
period_before_table: true,
56225627
events: vec![TriggerEvent::Delete],
56235628
table_name: ObjectName::from(vec![Ident::new("accounts")]),
56245629
referenced_table_name: None,
@@ -5633,6 +5638,7 @@ fn parse_create_instead_of_delete_trigger() {
56335638
args: None,
56345639
},
56355640
}),
5641+
statements_as: false,
56365642
statements: None,
56375643
characteristics: None,
56385644
};
@@ -5649,6 +5655,7 @@ fn parse_create_trigger_with_multiple_events_and_deferrable() {
56495655
is_constraint: true,
56505656
name: ObjectName::from(vec![Ident::new("check_multiple_events")]),
56515657
period: TriggerPeriod::Before,
5658+
period_before_table: true,
56525659
events: vec![
56535660
TriggerEvent::Insert,
56545661
TriggerEvent::Update(vec![]),
@@ -5667,6 +5674,7 @@ fn parse_create_trigger_with_multiple_events_and_deferrable() {
56675674
args: None,
56685675
},
56695676
}),
5677+
statements_as: false,
56705678
statements: None,
56715679
characteristics: Some(ConstraintCharacteristics {
56725680
deferrable: Some(true),
@@ -5687,6 +5695,7 @@ fn parse_create_trigger_with_referencing() {
56875695
is_constraint: false,
56885696
name: ObjectName::from(vec![Ident::new("check_referencing")]),
56895697
period: TriggerPeriod::Before,
5698+
period_before_table: true,
56905699
events: vec![TriggerEvent::Insert],
56915700
table_name: ObjectName::from(vec![Ident::new("accounts")]),
56925701
referenced_table_name: None,
@@ -5712,6 +5721,7 @@ fn parse_create_trigger_with_referencing() {
57125721
args: None,
57135722
},
57145723
}),
5724+
statements_as: false,
57155725
statements: None,
57165726
characteristics: None,
57175727
};
@@ -5994,6 +6004,7 @@ fn parse_trigger_related_functions() {
59946004
is_constraint: false,
59956005
name: ObjectName::from(vec![Ident::new("emp_stamp")]),
59966006
period: TriggerPeriod::Before,
6007+
period_before_table: true,
59976008
events: vec![TriggerEvent::Insert, TriggerEvent::Update(vec![])],
59986009
table_name: ObjectName::from(vec![Ident::new("emp")]),
59996010
referenced_table_name: None,
@@ -6005,9 +6016,10 @@ fn parse_trigger_related_functions() {
60056016
exec_type: TriggerExecBodyType::Function,
60066017
func_desc: FunctionDesc {
60076018
name: ObjectName::from(vec![Ident::new("emp_stamp")]),
6008-
args: None,
6019+
args: Some(vec![]),
60096020
}
60106021
}),
6022+
statements_as: false,
60116023
statements: None,
60126024
characteristics: None
60136025
}

0 commit comments

Comments
 (0)