Skip to content

Commit 183bc7c

Browse files
authored
Improve MySQL CREATE TRIGGER parsing (apache#1998)
1 parent 698154d commit 183bc7c

File tree

7 files changed

+62
-19
lines changed

7 files changed

+62
-19
lines changed

src/ast/mod.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3962,6 +3962,15 @@ pub enum Statement {
39623962
/// EXECUTE FUNCTION trigger_function();
39633963
/// ```
39643964
period: TriggerPeriod,
3965+
/// Whether the trigger period was specified before the target table name.
3966+
///
3967+
/// ```sql
3968+
/// -- period_before_table == true: Postgres, MySQL, and standard SQL
3969+
/// CREATE TRIGGER t BEFORE INSERT ON table_name ...;
3970+
/// -- period_before_table == false: MSSQL
3971+
/// CREATE TRIGGER t ON table_name BEFORE INSERT ...;
3972+
/// ```
3973+
period_before_table: bool,
39653974
/// Multiple events can be specified using OR, such as `INSERT`, `UPDATE`, `DELETE`, or `TRUNCATE`.
39663975
events: Vec<TriggerEvent>,
39673976
/// The table on which the trigger is to be created.
@@ -3980,6 +3989,8 @@ pub enum Statement {
39803989
condition: Option<Expr>,
39813990
/// Execute logic block
39823991
exec_body: Option<TriggerExecBody>,
3992+
/// For MSSQL and dialects where statements are preceded by `AS`
3993+
statements_as: bool,
39833994
/// For SQL dialects with statement(s) for a body
39843995
statements: Option<ConditionalStatements>,
39853996
/// The characteristic of the trigger, which include whether the trigger is `DEFERRABLE`, `INITIALLY DEFERRED`, or `INITIALLY IMMEDIATE`,
@@ -4944,6 +4955,7 @@ impl fmt::Display for Statement {
49444955
or_replace,
49454956
is_constraint,
49464957
name,
4958+
period_before_table,
49474959
period,
49484960
events,
49494961
table_name,
@@ -4953,6 +4965,7 @@ impl fmt::Display for Statement {
49534965
condition,
49544966
include_each,
49554967
exec_body,
4968+
statements_as,
49564969
statements,
49574970
characteristics,
49584971
} => {
@@ -4964,7 +4977,7 @@ impl fmt::Display for Statement {
49644977
is_constraint = if *is_constraint { "CONSTRAINT " } else { "" },
49654978
)?;
49664979

4967-
if exec_body.is_some() {
4980+
if *period_before_table {
49684981
write!(f, "{period}")?;
49694982
if !events.is_empty() {
49704983
write!(f, " {}", display_separated(events, " OR "))?;
@@ -5002,7 +5015,10 @@ impl fmt::Display for Statement {
50025015
write!(f, " EXECUTE {exec_body}")?;
50035016
}
50045017
if let Some(statements) = statements {
5005-
write!(f, " AS {statements}")?;
5018+
if *statements_as {
5019+
write!(f, " AS")?;
5020+
}
5021+
write!(f, " {statements}")?;
50065022
}
50075023
Ok(())
50085024
}

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: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5593,25 +5593,31 @@ impl<'a> Parser<'a> {
55935593
.then(|| self.parse_expr())
55945594
.transpose()?;
55955595

5596-
self.expect_keyword_is(Keyword::EXECUTE)?;
5597-
5598-
let exec_body = self.parse_trigger_exec_body()?;
5596+
let mut exec_body = None;
5597+
let mut statements = None;
5598+
if self.parse_keyword(Keyword::EXECUTE) {
5599+
exec_body = Some(self.parse_trigger_exec_body()?);
5600+
} else {
5601+
statements = Some(self.parse_conditional_statements(&[Keyword::END])?);
5602+
}
55995603

56005604
Ok(Statement::CreateTrigger {
56015605
or_alter,
56025606
or_replace,
56035607
is_constraint,
56045608
name,
56055609
period,
5610+
period_before_table: true,
56065611
events,
56075612
table_name,
56085613
referenced_table_name,
56095614
referencing,
56105615
trigger_object,
56115616
include_each,
56125617
condition,
5613-
exec_body: Some(exec_body),
5614-
statements: None,
5618+
exec_body,
5619+
statements_as: false,
5620+
statements,
56155621
characteristics,
56165622
})
56175623
}
@@ -6537,7 +6543,7 @@ impl<'a> Parser<'a> {
65376543

65386544
let args = if self.consume_token(&Token::LParen) {
65396545
if self.consume_token(&Token::RParen) {
6540-
None
6546+
Some(vec![])
65416547
} else {
65426548
let args = self.parse_comma_separated(Parser::parse_function_arg)?;
65436549
self.expect_token(&Token::RParen)?;
@@ -9307,10 +9313,10 @@ impl<'a> Parser<'a> {
93079313
}),
93089314
}))
93099315
} else {
9310-
return self.expected_ref(
9316+
self.expected_ref(
93119317
"{RENAME TO | { RENAME | ADD } VALUE}",
93129318
self.peek_token_ref(),
9313-
);
9319+
)
93149320
}
93159321
}
93169322

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)