Skip to content

Commit a29baa3

Browse files
Merge sqlite-trigger into future-main
2 parents ade4082 + cb9ec89 commit a29baa3

File tree

8 files changed

+375
-45
lines changed

8 files changed

+375
-45
lines changed

src/ast/ddl.rs

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3199,6 +3199,26 @@ impl Spanned for RenameTableNameKind {
31993199
}
32003200
}
32013201

3202+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
3203+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3204+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
3205+
/// Whether the syntax used for the trigger object (ROW or STATEMENT) is `FOR` or `FOR EACH`.
3206+
pub enum TriggerObjectKind {
3207+
/// The `FOR` syntax is used.
3208+
For(TriggerObject),
3209+
/// The `FOR EACH` syntax is used.
3210+
ForEach(TriggerObject),
3211+
}
3212+
3213+
impl Display for TriggerObjectKind {
3214+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3215+
match self {
3216+
TriggerObjectKind::For(obj) => write!(f, "FOR {obj}"),
3217+
TriggerObjectKind::ForEach(obj) => write!(f, "FOR EACH {obj}"),
3218+
}
3219+
}
3220+
}
3221+
32023222
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
32033223
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32043224
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
@@ -3220,6 +3240,23 @@ pub struct CreateTrigger {
32203240
///
32213241
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql?view=sql-server-ver16#arguments)
32223242
pub or_alter: bool,
3243+
/// True if this is a temporary trigger.
3244+
///
3245+
/// Examples:
3246+
///
3247+
/// ```sql
3248+
/// CREATE TEMP TRIGGER trigger_name
3249+
/// ```
3250+
///
3251+
/// or
3252+
///
3253+
/// ```sql
3254+
/// CREATE TEMPORARY TRIGGER trigger_name;
3255+
/// CREATE TEMP TRIGGER trigger_name;
3256+
/// ```
3257+
///
3258+
/// [SQLite](https://sqlite.org/lang_createtrigger.html#temp_triggers_on_non_temp_tables)
3259+
pub temporary: bool,
32233260
/// The `OR REPLACE` clause is used to re-create the trigger if it already exists.
32243261
///
32253262
/// Example:
@@ -3264,6 +3301,8 @@ pub struct CreateTrigger {
32643301
/// ```
32653302
pub period: TriggerPeriod,
32663303
/// Whether the trigger period was specified before the target table name.
3304+
/// This does not refer to whether the period is BEFORE, AFTER, or INSTEAD OF,
3305+
/// but rather the position of the period clause in relation to the table name.
32673306
///
32683307
/// ```sql
32693308
/// -- period_before_table == true: Postgres, MySQL, and standard SQL
@@ -3283,9 +3322,9 @@ pub struct CreateTrigger {
32833322
pub referencing: Vec<TriggerReferencing>,
32843323
/// This specifies whether the trigger function should be fired once for
32853324
/// every row affected by the trigger event, or just once per SQL statement.
3286-
pub trigger_object: TriggerObject,
3287-
/// Whether to include the `EACH` term of the `FOR EACH`, as it is optional syntax.
3288-
pub include_each: bool,
3325+
/// This is optional in some SQL dialects, such as SQLite, and if not specified, in
3326+
/// those cases, the implied default is `FOR EACH ROW`.
3327+
pub trigger_object: Option<TriggerObjectKind>,
32893328
/// Triggering conditions
32903329
pub condition: Option<Expr>,
32913330
/// Execute logic block
@@ -3302,6 +3341,7 @@ impl Display for CreateTrigger {
33023341
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33033342
let CreateTrigger {
33043343
or_alter,
3344+
temporary,
33053345
or_replace,
33063346
is_constraint,
33073347
name,
@@ -3313,15 +3353,15 @@ impl Display for CreateTrigger {
33133353
referencing,
33143354
trigger_object,
33153355
condition,
3316-
include_each,
33173356
exec_body,
33183357
statements_as,
33193358
statements,
33203359
characteristics,
33213360
} = self;
33223361
write!(
33233362
f,
3324-
"CREATE {or_alter}{or_replace}{is_constraint}TRIGGER {name} ",
3363+
"CREATE {temporary}{or_alter}{or_replace}{is_constraint}TRIGGER {name} ",
3364+
temporary = if *temporary { "TEMPORARY " } else { "" },
33253365
or_alter = if *or_alter { "OR ALTER " } else { "" },
33263366
or_replace = if *or_replace { "OR REPLACE " } else { "" },
33273367
is_constraint = if *is_constraint { "CONSTRAINT " } else { "" },
@@ -3353,10 +3393,8 @@ impl Display for CreateTrigger {
33533393
write!(f, " REFERENCING {}", display_separated(referencing, " "))?;
33543394
}
33553395

3356-
if *include_each {
3357-
write!(f, " FOR EACH {trigger_object}")?;
3358-
} else if exec_body.is_some() {
3359-
write!(f, " FOR {trigger_object}")?;
3396+
if let Some(trigger_object) = trigger_object {
3397+
write!(f, " {trigger_object}")?;
33603398
}
33613399
if let Some(condition) = condition {
33623400
write!(f, " WHEN {condition}")?;

src/ast/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ pub use self::ddl::{
6969
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
7070
IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition,
7171
ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, TableConstraint,
72-
TagsColumnOption, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation,
73-
ViewColumnDef,
72+
TagsColumnOption, TriggerObjectKind, UserDefinedTypeCompositeAttributeDef,
73+
UserDefinedTypeRepresentation, ViewColumnDef,
7474
};
7575
pub use self::dml::{Delete, Insert};
7676
pub use self::operator::{BinaryOperator, UnaryOperator};

src/dialect/mssql.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use crate::ast::helpers::attached_token::AttachedToken;
1919
use crate::ast::{
2020
BeginEndStatements, ConditionalStatementBlock, ConditionalStatements, CreateTrigger,
21-
GranteesType, IfStatement, Statement, TriggerObject,
21+
GranteesType, IfStatement, Statement,
2222
};
2323
use crate::dialect::Dialect;
2424
use crate::keywords::{self, Keyword};
@@ -254,6 +254,7 @@ impl MsSqlDialect {
254254

255255
Ok(CreateTrigger {
256256
or_alter,
257+
temporary: false,
257258
or_replace: false,
258259
is_constraint: false,
259260
name,
@@ -263,8 +264,7 @@ impl MsSqlDialect {
263264
table_name,
264265
referenced_table_name: None,
265266
referencing: Vec::new(),
266-
trigger_object: TriggerObject::Statement,
267-
include_each: false,
267+
trigger_object: None,
268268
condition: None,
269269
exec_body: None,
270270
statements_as: true,

src/parser/mod.rs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4750,9 +4750,9 @@ impl<'a> Parser<'a> {
47504750
} else if self.parse_keyword(Keyword::DOMAIN) {
47514751
self.parse_create_domain()
47524752
} else if self.parse_keyword(Keyword::TRIGGER) {
4753-
self.parse_create_trigger(or_alter, or_replace, false)
4753+
self.parse_create_trigger(temporary, or_alter, or_replace, false)
47544754
} else if self.parse_keywords(&[Keyword::CONSTRAINT, Keyword::TRIGGER]) {
4755-
self.parse_create_trigger(or_alter, or_replace, true)
4755+
self.parse_create_trigger(temporary, or_alter, or_replace, true)
47564756
} else if self.parse_keyword(Keyword::MACRO) {
47574757
self.parse_create_macro(or_replace, temporary)
47584758
} else if self.parse_keyword(Keyword::SECRET) {
@@ -5548,7 +5548,8 @@ impl<'a> Parser<'a> {
55485548
/// DROP TRIGGER [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ]
55495549
/// ```
55505550
pub fn parse_drop_trigger(&mut self) -> Result<Statement, ParserError> {
5551-
if !dialect_of!(self is PostgreSqlDialect | GenericDialect | MySqlDialect | MsSqlDialect) {
5551+
if !dialect_of!(self is PostgreSqlDialect | SQLiteDialect | GenericDialect | MySqlDialect | MsSqlDialect)
5552+
{
55525553
self.prev_token();
55535554
return self.expected("an object type after DROP", self.peek_token());
55545555
}
@@ -5576,11 +5577,13 @@ impl<'a> Parser<'a> {
55765577

55775578
pub fn parse_create_trigger(
55785579
&mut self,
5580+
temporary: bool,
55795581
or_alter: bool,
55805582
or_replace: bool,
55815583
is_constraint: bool,
55825584
) -> Result<Statement, ParserError> {
5583-
if !dialect_of!(self is PostgreSqlDialect | GenericDialect | MySqlDialect | MsSqlDialect) {
5585+
if !dialect_of!(self is PostgreSqlDialect | SQLiteDialect | GenericDialect | MySqlDialect | MsSqlDialect)
5586+
{
55845587
self.prev_token();
55855588
return self.expected("an object type after CREATE", self.peek_token());
55865589
}
@@ -5607,14 +5610,27 @@ impl<'a> Parser<'a> {
56075610
}
56085611
}
56095612

5610-
self.expect_keyword_is(Keyword::FOR)?;
5611-
let include_each = self.parse_keyword(Keyword::EACH);
5612-
let trigger_object =
5613-
match self.expect_one_of_keywords(&[Keyword::ROW, Keyword::STATEMENT])? {
5614-
Keyword::ROW => TriggerObject::Row,
5615-
Keyword::STATEMENT => TriggerObject::Statement,
5616-
_ => unreachable!(),
5617-
};
5613+
let trigger_object = if self.parse_keyword(Keyword::FOR) {
5614+
let include_each = self.parse_keyword(Keyword::EACH);
5615+
let trigger_object =
5616+
match self.expect_one_of_keywords(&[Keyword::ROW, Keyword::STATEMENT])? {
5617+
Keyword::ROW => TriggerObject::Row,
5618+
Keyword::STATEMENT => TriggerObject::Statement,
5619+
_ => unreachable!(),
5620+
};
5621+
5622+
Some(if include_each {
5623+
TriggerObjectKind::ForEach(trigger_object)
5624+
} else {
5625+
TriggerObjectKind::For(trigger_object)
5626+
})
5627+
} else {
5628+
if !dialect_of!(self is SQLiteDialect ) {
5629+
self.expect_keyword_is(Keyword::FOR)?;
5630+
}
5631+
5632+
None
5633+
};
56185634

56195635
let condition = self
56205636
.parse_keyword(Keyword::WHEN)
@@ -5629,8 +5645,9 @@ impl<'a> Parser<'a> {
56295645
statements = Some(self.parse_conditional_statements(&[Keyword::END])?);
56305646
}
56315647

5632-
Ok(Statement::CreateTrigger(CreateTrigger {
5648+
Ok(CreateTrigger {
56335649
or_alter,
5650+
temporary,
56345651
or_replace,
56355652
is_constraint,
56365653
name,
@@ -5641,13 +5658,13 @@ impl<'a> Parser<'a> {
56415658
referenced_table_name,
56425659
referencing,
56435660
trigger_object,
5644-
include_each,
56455661
condition,
56465662
exec_body,
56475663
statements_as: false,
56485664
statements,
56495665
characteristics,
5650-
}))
5666+
}
5667+
.into())
56515668
}
56525669

56535670
pub fn parse_trigger_period(&mut self) -> Result<TriggerPeriod, ParserError> {

tests/sqlparser_mssql.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2386,6 +2386,7 @@ fn parse_create_trigger() {
23862386
create_stmt,
23872387
Statement::CreateTrigger(CreateTrigger {
23882388
or_alter: true,
2389+
temporary: false,
23892390
or_replace: false,
23902391
is_constraint: false,
23912392
name: ObjectName::from(vec![Ident::new("reminder1")]),
@@ -2395,8 +2396,7 @@ fn parse_create_trigger() {
23952396
table_name: ObjectName::from(vec![Ident::new("Sales"), Ident::new("Customer")]),
23962397
referenced_table_name: None,
23972398
referencing: vec![],
2398-
trigger_object: TriggerObject::Statement,
2399-
include_each: false,
2399+
trigger_object: None,
24002400
condition: None,
24012401
exec_body: None,
24022402
statements_as: true,

tests/sqlparser_mysql.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4016,6 +4016,7 @@ fn parse_create_trigger() {
40164016
create_stmt,
40174017
Statement::CreateTrigger(CreateTrigger {
40184018
or_alter: false,
4019+
temporary: false,
40194020
or_replace: false,
40204021
is_constraint: false,
40214022
name: ObjectName::from(vec![Ident::new("emp_stamp")]),
@@ -4025,8 +4026,7 @@ fn parse_create_trigger() {
40254026
table_name: ObjectName::from(vec![Ident::new("emp")]),
40264027
referenced_table_name: None,
40274028
referencing: vec![],
4028-
trigger_object: TriggerObject::Row,
4029-
include_each: true,
4029+
trigger_object: Some(TriggerObjectKind::ForEach(TriggerObject::Row)),
40304030
condition: None,
40314031
exec_body: Some(TriggerExecBody {
40324032
exec_type: TriggerExecBodyType::Function,

tests/sqlparser_postgres.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5673,6 +5673,7 @@ fn parse_create_simple_before_insert_trigger() {
56735673
let sql = "CREATE TRIGGER check_insert BEFORE INSERT ON accounts FOR EACH ROW EXECUTE FUNCTION check_account_insert";
56745674
let expected = Statement::CreateTrigger(CreateTrigger {
56755675
or_alter: false,
5676+
temporary: false,
56765677
or_replace: false,
56775678
is_constraint: false,
56785679
name: ObjectName::from(vec![Ident::new("check_insert")]),
@@ -5682,8 +5683,7 @@ fn parse_create_simple_before_insert_trigger() {
56825683
table_name: ObjectName::from(vec![Ident::new("accounts")]),
56835684
referenced_table_name: None,
56845685
referencing: vec![],
5685-
trigger_object: TriggerObject::Row,
5686-
include_each: true,
5686+
trigger_object: Some(TriggerObjectKind::ForEach(TriggerObject::Row)),
56875687
condition: None,
56885688
exec_body: Some(TriggerExecBody {
56895689
exec_type: TriggerExecBodyType::Function,
@@ -5705,6 +5705,7 @@ fn parse_create_after_update_trigger_with_condition() {
57055705
let sql = "CREATE TRIGGER check_update AFTER UPDATE ON accounts FOR EACH ROW WHEN (NEW.balance > 10000) EXECUTE FUNCTION check_account_update";
57065706
let expected = Statement::CreateTrigger(CreateTrigger {
57075707
or_alter: false,
5708+
temporary: false,
57085709
or_replace: false,
57095710
is_constraint: false,
57105711
name: ObjectName::from(vec![Ident::new("check_update")]),
@@ -5714,8 +5715,7 @@ fn parse_create_after_update_trigger_with_condition() {
57145715
table_name: ObjectName::from(vec![Ident::new("accounts")]),
57155716
referenced_table_name: None,
57165717
referencing: vec![],
5717-
trigger_object: TriggerObject::Row,
5718-
include_each: true,
5718+
trigger_object: Some(TriggerObjectKind::ForEach(TriggerObject::Row)),
57195719
condition: Some(Expr::Nested(Box::new(Expr::BinaryOp {
57205720
left: Box::new(Expr::CompoundIdentifier(vec![
57215721
Ident::new("NEW"),
@@ -5744,6 +5744,7 @@ fn parse_create_instead_of_delete_trigger() {
57445744
let sql = "CREATE TRIGGER check_delete INSTEAD OF DELETE ON accounts FOR EACH ROW EXECUTE FUNCTION check_account_deletes";
57455745
let expected = Statement::CreateTrigger(CreateTrigger {
57465746
or_alter: false,
5747+
temporary: false,
57475748
or_replace: false,
57485749
is_constraint: false,
57495750
name: ObjectName::from(vec![Ident::new("check_delete")]),
@@ -5753,8 +5754,7 @@ fn parse_create_instead_of_delete_trigger() {
57535754
table_name: ObjectName::from(vec![Ident::new("accounts")]),
57545755
referenced_table_name: None,
57555756
referencing: vec![],
5756-
trigger_object: TriggerObject::Row,
5757-
include_each: true,
5757+
trigger_object: Some(TriggerObjectKind::ForEach(TriggerObject::Row)),
57585758
condition: None,
57595759
exec_body: Some(TriggerExecBody {
57605760
exec_type: TriggerExecBodyType::Function,
@@ -5776,6 +5776,7 @@ fn parse_create_trigger_with_multiple_events_and_deferrable() {
57765776
let sql = "CREATE CONSTRAINT TRIGGER check_multiple_events BEFORE INSERT OR UPDATE OR DELETE ON accounts DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_account_changes";
57775777
let expected = Statement::CreateTrigger(CreateTrigger {
57785778
or_alter: false,
5779+
temporary: false,
57795780
or_replace: false,
57805781
is_constraint: true,
57815782
name: ObjectName::from(vec![Ident::new("check_multiple_events")]),
@@ -5789,8 +5790,7 @@ fn parse_create_trigger_with_multiple_events_and_deferrable() {
57895790
table_name: ObjectName::from(vec![Ident::new("accounts")]),
57905791
referenced_table_name: None,
57915792
referencing: vec![],
5792-
trigger_object: TriggerObject::Row,
5793-
include_each: true,
5793+
trigger_object: Some(TriggerObjectKind::ForEach(TriggerObject::Row)),
57945794
condition: None,
57955795
exec_body: Some(TriggerExecBody {
57965796
exec_type: TriggerExecBodyType::Function,
@@ -5816,6 +5816,7 @@ fn parse_create_trigger_with_referencing() {
58165816
let sql = "CREATE TRIGGER check_referencing BEFORE INSERT ON accounts REFERENCING NEW TABLE AS new_accounts OLD TABLE AS old_accounts FOR EACH ROW EXECUTE FUNCTION check_account_referencing";
58175817
let expected = Statement::CreateTrigger(CreateTrigger {
58185818
or_alter: false,
5819+
temporary: false,
58195820
or_replace: false,
58205821
is_constraint: false,
58215822
name: ObjectName::from(vec![Ident::new("check_referencing")]),
@@ -5836,8 +5837,7 @@ fn parse_create_trigger_with_referencing() {
58365837
transition_relation_name: ObjectName::from(vec![Ident::new("old_accounts")]),
58375838
},
58385839
],
5839-
trigger_object: TriggerObject::Row,
5840-
include_each: true,
5840+
trigger_object: Some(TriggerObjectKind::ForEach(TriggerObject::Row)),
58415841
condition: None,
58425842
exec_body: Some(TriggerExecBody {
58435843
exec_type: TriggerExecBodyType::Function,
@@ -6132,6 +6132,7 @@ fn parse_trigger_related_functions() {
61326132
create_trigger,
61336133
Statement::CreateTrigger(CreateTrigger {
61346134
or_alter: false,
6135+
temporary: false,
61356136
or_replace: false,
61366137
is_constraint: false,
61376138
name: ObjectName::from(vec![Ident::new("emp_stamp")]),
@@ -6141,8 +6142,7 @@ fn parse_trigger_related_functions() {
61416142
table_name: ObjectName::from(vec![Ident::new("emp")]),
61426143
referenced_table_name: None,
61436144
referencing: vec![],
6144-
trigger_object: TriggerObject::Row,
6145-
include_each: true,
6145+
trigger_object: Some(TriggerObjectKind::ForEach(TriggerObject::Row)),
61466146
condition: None,
61476147
exec_body: Some(TriggerExecBody {
61486148
exec_type: TriggerExecBodyType::Function,

0 commit comments

Comments
 (0)